[SCM] qmidiarp/master: Imported Upstream version 0.4.1

alessio at users.alioth.debian.org alessio at users.alioth.debian.org
Wed Jun 1 11:11:30 UTC 2011


The following commit has been merged in the master branch:
commit d7f51043521f3ffc51696d8be6e66bffbf1dc4c5
Author: Alessio Treglia <alessio at debian.org>
Date:   Wed Jun 1 13:02:10 2011 +0200

    Imported Upstream version 0.4.1

diff --git a/AUTHORS b/AUTHORS
index 547b27f..1557818 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,6 +1,13 @@
 Authors of qmidiarp
 ---------------------------------
 
-Matthias Nagorni <mana at suse.de>
 Frank Kober <goemusic at yahoo.fr>
 Guido Scholz <guido.scholz at bayernline.de>
+Matthias Nagorni <mana at suse.de>
+
+
+Translations
+---------------------------------
+Robert Dietrich - de
+Pedro Lopez-Cabanillas - es
+Pavel Fric - cs
diff --git a/ChangeLog b/ChangeLog
index 926237d..3039a4b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2011-01-23  Frank Kober  <goemusic at yahoo.fr>
+	* midilfo.*, lfowidget.*, lfoscreen.*, mainwindow.cpp : LFO can
+	now record controller data sent to its input. The record mode is set
+	by a toolbutton equipped with MIDI Learn. Usage idea is to move a
+	controller while pressing an assigned record button on an external
+	controller to have a controller phrase looper.
+	At this time the maximum resolution of recording is limited to 16 but
+	will be extended.
+	
+2011-01-19  Frank Kober  <goemusic at yahoo.fr>
+	* midiarp/lfo/seq.*, *screen.*: mainwindow.cpp: playhead cursor
+	implemented for all modules. The former packet pre-scheduling has
+	been changed so that only 1 event is scheduled per alsa echo. The
+	LFO still uses increasing packet size for resolutions > 16 to avoid
+	excessive drawing cycles.
+	
+2011-01-18  Frank Kober  <goemusic at yahoo.fr>
+	* version changed to 0.4.0
+	
+2011-01-08  Frank Kober  <goemusic at yahoo.fr>
+	* src/translations/qmidiarp_es.ts, src/Makefile.am: 
+	spanish translation added, provided by Pedro Lopez-Cabanillas
+	* groovewidget.cpp, slider.cpp: font-size-sensitive widths improved
+
 +++ Release 0.3.9 (2011-01-05) +++
 
 2011-01-04  Frank Kober  <goemusic at yahoo.fr>
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..80f73db
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,1721 @@
+# Doxyfile 1.7.3
+
+# 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 (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = QMidiArp
+
+# 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         = 0.4.0
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "A MIDI Arpeggiator, LFO and Step Sequencer for ALSA"
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           = html/qmidiarp_logo_med2.png
+
+# 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       = doxygen-doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# 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:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# 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
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the \
+                         "This function"
+
+# 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    = NO
+
+# 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        = YES
+
+# 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. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# 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 regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = 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
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = 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
+
+# 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                =
+
+# 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.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = 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
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# 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         = NO
+
+# 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
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# 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 classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# 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
+
+# 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
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_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 SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# 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 the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# 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
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# 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 macro consists 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 macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# 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
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# 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
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# 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. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# 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                  = src
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# 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 *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.f90 \
+                         *.f \
+                         *.for \
+                         *.vhd \
+                         *.vhdl \
+                         *.cpp
+
+# 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              = NO
+
+# 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                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# 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. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# 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.  If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.  Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.  The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# 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 (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# 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.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = 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 REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = 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
+
+#---------------------------------------------------------------------------
+# 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          =
+
+#---------------------------------------------------------------------------
+# 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            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# 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. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# 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 HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# 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 compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# 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 CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# 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
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# 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 [0,1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+# Note that a value of 0 will completely suppress the enum values from
+# appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value 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, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# 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
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# 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           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# 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, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4
+
+# 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         = YES
+
+# 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           = YES
+
+# 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
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = 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 optimized 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             = rtf
+
+# 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 assignments. 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             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# 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.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML 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 `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# 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 Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.  This is useful
+# if you want to understand what is going on.  On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# 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_DEFINED 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. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF 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 that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#   TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+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
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# 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 also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# 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 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
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will write a font called Helvetica to the output
+# directory and reference it in all dot files that doxygen generates.
+# When you want a differently looking font you can specify the font name
+# using DOT_FONTNAME. You need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# 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            = YES
+
+# 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 the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# 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 CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, svg, gif or svg.
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# 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 in 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 MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# 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 intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/Makefile.am b/Makefile.am
index 67cacc8..7a0c860 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,12 @@
 # Makefile.am for qmidiarp
 #
 SUBDIRS = man examples src
+
+svgdatadir=@datadir@/icons/hicolor/scalable/apps
+dist_svgdata_DATA = qmidiarp.svg
+
+applicationsdir = @datadir@/applications
+dist_applications_DATA = qmidiarp.desktop
+
+include $(top_srcdir)/aminclude.am
+EXTRA_DIST = Doxyfile html/qmidiarp_logo_med2.png
diff --git a/Makefile.in b/Makefile.in
index 0ce5763..adfed5e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -14,6 +14,37 @@
 # PARTICULAR PURPOSE.
 
 @SET_MAKE@
+
+# Copyright (C) 2004 Oren Ben-Kiki
+# This file is distributed under the same terms as the Automake macro files.
+
+# Generate automatic documentation using Doxygen. Goals and variables values
+# are controlled by the various DX_COND_??? conditionals set by autoconf.
+#
+# The provided goals are:
+# doxygen-doc: Generate all doxygen documentation.
+# doxygen-run: Run doxygen, which will generate some of the documentation
+#              (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post
+#              processing required for the rest of it (PS, PDF, and some MAN).
+# doxygen-man: Rename some doxygen generated man pages.
+# doxygen-ps: Generate doxygen PostScript documentation.
+# doxygen-pdf: Generate doxygen PDF documentation.
+#
+# Note that by default these are not integrated into the automake goals. If
+# doxygen is used to generate man pages, you can achieve this integration by
+# setting man3_MANS to the list of man pages generated and then adding the
+# dependency:
+#
+#   $(man3_MANS): doxygen-doc
+#
+# This will cause make to run doxygen and generate all the documentation.
+#
+# The following variable is intended for use in Makefile.am:
+#
+# DX_CLEANFILES = everything to clean.
+#
+# This is usually added to MOSTLYCLEANFILES.
+
 VPATH = @srcdir@
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgincludedir = $(includedir)/@PACKAGE@
@@ -31,12 +62,15 @@ POST_INSTALL = :
 NORMAL_UNINSTALL = :
 PRE_UNINSTALL = :
 POST_UNINSTALL = :
+DIST_COMMON = README $(am__configure_deps) $(dist_applications_DATA) \
+	$(dist_svgdata_DATA) $(srcdir)/Makefile.am \
+	$(srcdir)/Makefile.in $(top_srcdir)/aminclude.am \
+	$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
+	depcomp install-sh missing
 subdir = .
-DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
-	$(srcdir)/Makefile.in $(top_srcdir)/configure AUTHORS COPYING \
-	ChangeLog INSTALL NEWS depcomp install-sh missing
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
@@ -54,6 +88,30 @@ RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
 	install-pdf-recursive install-ps-recursive install-recursive \
 	installcheck-recursive installdirs-recursive pdf-recursive \
 	ps-recursive uninstall-recursive
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(applicationsdir)" \
+	"$(DESTDIR)$(svgdatadir)"
+DATA = $(dist_applications_DATA) $(dist_svgdata_DATA)
 RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
   distclean-recursive maintainer-clean-recursive
 AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
@@ -115,6 +173,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
@@ -203,12 +286,41 @@ top_srcdir = @top_srcdir@
 # Makefile.am for qmidiarp
 #
 SUBDIRS = man examples src
+svgdatadir = @datadir@/icons/hicolor/scalable/apps
+dist_svgdata_DATA = qmidiarp.svg
+applicationsdir = @datadir@/applications
+dist_applications_DATA = qmidiarp.desktop
+ at DX_COND_doc_TRUE@@DX_COND_html_TRUE at DX_CLEAN_HTML = @DX_DOCDIR@/html
+ at DX_COND_chm_TRUE@@DX_COND_doc_TRUE at DX_CLEAN_CHM = @DX_DOCDIR@/chm
+ at DX_COND_chi_TRUE@@DX_COND_chm_TRUE@@DX_COND_doc_TRUE at DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE at .chi
+ at DX_COND_doc_TRUE@@DX_COND_man_TRUE at DX_CLEAN_MAN = @DX_DOCDIR@/man
+ at DX_COND_doc_TRUE@@DX_COND_rtf_TRUE at DX_CLEAN_RTF = @DX_DOCDIR@/rtf
+ at DX_COND_doc_TRUE@@DX_COND_xml_TRUE at DX_CLEAN_XML = @DX_DOCDIR@/xml
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE at DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE at .ps
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE at DX_PS_GOAL = doxygen-ps
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE at DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE at .pdf
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE at DX_PDF_GOAL = doxygen-pdf
+ at DX_COND_doc_TRUE@@DX_COND_latex_TRUE at DX_CLEAN_LATEX = @DX_DOCDIR@/latex
+ at DX_COND_doc_TRUE@DX_CLEANFILES = \
+ at DX_COND_doc_TRUE@    @DX_DOCDIR@/@PACKAGE at .tag \
+ at DX_COND_doc_TRUE@    -r \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_HTML) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_CHM) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_CHI) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_MAN) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_RTF) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_XML) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_PS) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_PDF) \
+ at DX_COND_doc_TRUE@    $(DX_CLEAN_LATEX)
+
+EXTRA_DIST = Doxyfile html/qmidiarp_logo_med2.png
 all: all-recursive
 
 .SUFFIXES:
 am--refresh:
 	@:
-$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am $(top_srcdir)/aminclude.am $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
 	    *$$dep*) \
@@ -240,6 +352,46 @@ $(top_srcdir)/configure:  $(am__configure_deps)
 $(ACLOCAL_M4):  $(am__aclocal_m4_deps)
 	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
 $(am__aclocal_m4_deps):
+install-dist_applicationsDATA: $(dist_applications_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(applicationsdir)" || $(MKDIR_P) "$(DESTDIR)$(applicationsdir)"
+	@list='$(dist_applications_DATA)'; test -n "$(applicationsdir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(applicationsdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(applicationsdir)" || exit $$?; \
+	done
+
+uninstall-dist_applicationsDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dist_applications_DATA)'; test -n "$(applicationsdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(applicationsdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(applicationsdir)" && rm -f $$files
+install-dist_svgdataDATA: $(dist_svgdata_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(svgdatadir)" || $(MKDIR_P) "$(DESTDIR)$(svgdatadir)"
+	@list='$(dist_svgdata_DATA)'; test -n "$(svgdatadir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(svgdatadir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(svgdatadir)" || exit $$?; \
+	done
+
+uninstall-dist_svgdataDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dist_svgdata_DATA)'; test -n "$(svgdatadir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(svgdatadir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(svgdatadir)" && rm -f $$files
 
 # This directory's subdirectories are mostly independent; you can cd
 # into them and run `make' without going through this Makefile.
@@ -553,9 +705,12 @@ distcleancheck: distclean
 	       exit 1; } >&2
 check-am: all-am
 check: check-recursive
-all-am: Makefile
+all-am: Makefile $(DATA)
 installdirs: installdirs-recursive
 installdirs-am:
+	for dir in "$(DESTDIR)$(applicationsdir)" "$(DESTDIR)$(svgdatadir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
 install: install-recursive
 install-exec: install-exec-recursive
 install-data: install-data-recursive
@@ -602,7 +757,8 @@ info: info-recursive
 
 info-am:
 
-install-data-am:
+install-data-am: install-dist_applicationsDATA \
+	install-dist_svgdataDATA
 
 install-dvi: install-dvi-recursive
 
@@ -648,7 +804,8 @@ ps: ps-recursive
 
 ps-am:
 
-uninstall-am:
+uninstall-am: uninstall-dist_applicationsDATA \
+	uninstall-dist_svgdataDATA
 
 .MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
 	install-am install-strip tags-recursive
@@ -660,14 +817,62 @@ uninstall-am:
 	distclean distclean-generic distclean-tags distcleancheck \
 	distdir distuninstallcheck dvi dvi-am html html-am info \
 	info-am install install-am install-data install-data-am \
+	install-dist_applicationsDATA install-dist_svgdataDATA \
 	install-dvi install-dvi-am install-exec install-exec-am \
 	install-html install-html-am install-info install-info-am \
 	install-man install-pdf install-pdf-am install-ps \
 	install-ps-am install-strip installcheck installcheck-am \
 	installdirs installdirs-am maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
-	pdf-am ps ps-am tags tags-recursive uninstall uninstall-am
-
+	pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+	uninstall-dist_applicationsDATA uninstall-dist_svgdataDATA
+
+
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE at doxygen-ps: @DX_DOCDIR@/@PACKAGE at .ps
+
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@@DX_DOCDIR@/@PACKAGE at .ps: @DX_DOCDIR@/@PACKAGE at .tag
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	cd @DX_DOCDIR@/latex; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	$(DX_LATEX) refman.tex; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	$(MAKEINDEX_PATH) refman.idx; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	$(DX_LATEX) refman.tex; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	countdown=5; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	                  refman.log > /dev/null 2>&1 \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	   && test $$countdown -gt 0; do \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	    $(DX_LATEX) refman.tex; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	    countdown=`expr $$countdown - 1`; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	done; \
+ at DX_COND_doc_TRUE@@DX_COND_ps_TRUE@	$(DX_DVIPS) -o ../@PACKAGE at .ps refman.dvi
+
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE at doxygen-pdf: @DX_DOCDIR@/@PACKAGE at .pdf
+
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@@DX_DOCDIR@/@PACKAGE at .pdf: @DX_DOCDIR@/@PACKAGE at .tag
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	cd @DX_DOCDIR@/latex; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	$(DX_PDFLATEX) refman.tex; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	$(DX_MAKEINDEX) refman.idx; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	$(DX_PDFLATEX) refman.tex; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	countdown=5; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	                  refman.log > /dev/null 2>&1 \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	   && test $$countdown -gt 0; do \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	    $(DX_PDFLATEX) refman.tex; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	    countdown=`expr $$countdown - 1`; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	done; \
+ at DX_COND_doc_TRUE@@DX_COND_pdf_TRUE@	mv refman.pdf ../@PACKAGE at .pdf
+
+ at DX_COND_doc_TRUE@.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+ at DX_COND_doc_TRUE@.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+ at DX_COND_doc_TRUE@doxygen-run: @DX_DOCDIR@/@PACKAGE at .tag
+
+ at DX_COND_doc_TRUE@doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+ at DX_COND_doc_TRUE@@DX_DOCDIR@/@PACKAGE at .tag: $(DX_CONFIG) $(pkginclude_HEADERS)
+ at DX_COND_doc_TRUE@	rm -rf @DX_DOCDIR@
+ at DX_COND_doc_TRUE@	$(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/NEWS b/NEWS
index 4f9ac45..ae8fff8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,21 @@
+qmidiarp-0.4.1 (2011-05-29)
+
+New Features
+  o All modules now have a playhead cursor
+  o LFOs can record controller data by a MIDI-learnable Record button.
+  o Seq modules handle note events at the input for triggering
+  o Spanish translation thanks to Pedro Lopez-Cabanillas (plcl)
+  o Czech translation thanks to Pavel Fric
+  o Functional doxygen documentation available by "make doxygen-doc"
+  o New SVG icon and desktop file
+
+
 qmidiarp-0.3.9 (2011-01-05)
 
 New Features
   o Arpeggio pattern preset infrastructure
   o Synchronized MIDI LFO modules added
-    LFOs have calculated and drawable waveforms, selectable frequency, 
+    LFOs have calculated and drawable waveforms, selectable frequency,
     amplitude, offset, time resolution and length
   o Synchronized step sequencer modules added
     Step sequencer can be transposed and velocity-modulated by received
@@ -20,7 +32,7 @@ New Features
   o Event log entries are color-coded, optional MIDI Clock event display
   o Re-designed graphical user interface:  all modules and dialogs
     are dockable floatable windows, main and file icon toolbars added
-  o New .qmidiarprc file containing GUI settings, user arp patterns and 
+  o New .qmidiarprc file containing GUI settings, user arp patterns and
     last file path
   o Save and SaveAs functions with modification monitoring
   o All relevant session parameters stored in new .qmax XML session file
diff --git a/README b/README
index 94d472d..c774407 100644
--- a/README
+++ b/README
@@ -1,13 +1,27 @@
-QMidiArp by     Matthias Nagorni, (c)2004 by Novell,
-                Frank Kober, Guido Scholz (c)2009 - 2011
-         published under GNU General Public License
-         a copy of the License can be found in the COPYING file
+QMidiArp by Frank Kober, Guido Scholz (c)2009 - 2011
+            Matthias Nagorni          (c)2004 by Novell
+
+         published under the GNU General Public License.
+         A copy of the License can be found in the COPYING file
 ---------------------------------------------------------------
 
+
+Description
+-----------
 QMidiArp is an arpeggiator, sequencer and MIDI LFO for ALSA.
 For further information on general function please refer to the qmidiarp
 manual page.
 
+
+Dependencies
+------------
+You need the following development headers and libraries for building.
+
+libqt4-dev     (qt-devel)
+libasound2-dev (alsa-lib-devel)
+libjack-dev    (jackit-devel)
+
+
 Installation
 -------------
 qmidiarp uses autoconf/automake as build system. For short
@@ -19,7 +33,7 @@ qmidiarp uses autoconf/automake as build system. For short
 does the trick. Please refer to the INSTALL file for more information.
 
 
-Git check out
+Git check-out
 -------------
 If you start with a fresh Git checkout, please first apply
 
@@ -27,3 +41,22 @@ If you start with a fresh Git checkout, please first apply
 
 to get a proper configure script. For more instructions about compiling
 and installing this application please refer to the INSTALL file.
+
+
+Doxygen documentation
+---------------------
+If you have doxygen installed, you can use
+
+make doxygen-doc
+
+to produce functional html documentation of all classes. The html files
+are written to the doxygen-doc directory.
+
+
+Help
+----
+User documentation is provided at
+
+qmidiarp.sourceforge.net
+
+Please ask for help there, too.
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 0000000..93b12f4
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,321 @@
+# This file is part of Autoconf.                       -*- Autoconf -*-
+
+# Copyright (C) 2004 Oren Ben-Kiki
+# This file is distributed under the same terms as the Autoconf macro files.
+
+########## CHANGELOG ##################
+# 2009-01-14 Martin Mann
+# * DX_ARG_ABLE : new variable 'DX_FLAG_DX_CURRENT_FEATURE'
+# * DX_CLEAR_DEPEND : use of explicit variable 'DX_FLAG_DX_CURRENT_FEATURE'
+#   in AC_SUBST instead of 'DX_FLAG[]DX_CURRENT_FEATURE' which is rejected by
+#   newer autotools  
+
+# Generate automatic documentation using Doxygen. Works in concert with the
+# aminclude.m4 file and a compatible doxygen configuration file. Defines the
+# following public macros:
+#
+# DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature.
+# Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics,
+# 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI'
+# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF',
+# 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment
+# variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide'
+# paper size.
+#
+# By default, HTML, PDF and PS documentation is generated as this seems to be
+# the most popular and portable combination. MAN pages created by Doxygen are
+# usually problematic, though by picking an appropriate subset and doing some
+# massaging they might be better than nothing. CHM and RTF are specific for MS
+# (note that you can't generate both HTML and CHM at the same time). The XML is
+# rather useless unless you apply specialized post-processing to it.
+#
+# The macro mainly controls the default state of the feature. The use can
+# override the default by specifying --enable or --disable. The macros ensure
+# that contradictory flags are not given (e.g., --enable-doxygen-html and
+# --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.)
+# Finally, each feature will be automatically disabled (with a warning) if the
+# required programs are missing.
+#
+# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with
+# the following parameters: a one-word name for the project for use as a
+# filename base etc., an optional configuration file name (the default is
+# 'Doxyfile', the same as Doxygen's default), and an optional output directory
+# name (the default is 'doxygen-doc').
+
+## ----------##
+## Defaults. ##
+## ----------##
+
+DX_ENV=""
+AC_DEFUN([DX_FEATURE_doc],  ON)
+AC_DEFUN([DX_FEATURE_dot],  ON)
+AC_DEFUN([DX_FEATURE_man],  OFF)
+AC_DEFUN([DX_FEATURE_html], ON)
+AC_DEFUN([DX_FEATURE_chm],  OFF)
+AC_DEFUN([DX_FEATURE_chi],  OFF)
+AC_DEFUN([DX_FEATURE_rtf],  OFF)
+AC_DEFUN([DX_FEATURE_xml],  OFF)
+AC_DEFUN([DX_FEATURE_pdf],  ON)
+AC_DEFUN([DX_FEATURE_ps],   ON)
+
+## --------------- ##
+## Private macros. ##
+## --------------- ##
+
+# DX_ENV_APPEND(VARIABLE, VALUE)
+# ------------------------------
+# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen.
+AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])])
+
+# DX_DIRNAME_EXPR
+# ---------------
+# Expand into a shell expression prints the directory part of a path.
+AC_DEFUN([DX_DIRNAME_EXPR],
+         [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']])
+
+# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF)
+# -------------------------------------
+# Expands according to the M4 (static) status of the feature.
+AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])])
+
+# DX_REQUIRE_PROG(VARIABLE, PROGRAM)
+# ----------------------------------
+# Require the specified program to be found for the DX_CURRENT_FEATURE to work.
+AC_DEFUN([DX_REQUIRE_PROG], [
+AC_PATH_TOOL([$1], [$2])
+if test "$DX_FLAG_DX_CURRENT_FEATURE$$1" = 1; then
+    AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION])
+    AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0)
+fi
+])
+
+# DX_TEST_FEATURE(FEATURE)
+# ------------------------
+# Expand to a shell expression testing whether the feature is active.
+AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
+
+# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE)
+# -------------------------------------------------
+# Verify that a required features has the right state before trying to turn on
+# the DX_CURRENT_FEATURE.
+AC_DEFUN([DX_CHECK_DEPEND], [
+test "$DX_FLAG_$1" = "$2" \
+|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
+                            requires, contradicts) doxygen-DX_CURRENT_FEATURE])
+])
+
+# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
+# ----------------------------------------------------------
+# Turn off the DX_CURRENT_FEATURE if the required feature is off.
+AC_DEFUN([DX_CLEAR_DEPEND], [
+test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0)
+])
+
+
+# DX_FEATURE_ARG(FEATURE, DESCRIPTION,
+#                CHECK_DEPEND, CLEAR_DEPEND,
+#                REQUIRE, DO-IF-ON, DO-IF-OFF)
+# --------------------------------------------
+# Parse the command-line option controlling a feature. CHECK_DEPEND is called
+# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND),
+# otherwise CLEAR_DEPEND is called to turn off the default state if a required
+# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional
+# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and
+# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature.
+AC_DEFUN([DX_ARG_ABLE], [
+    AC_DEFUN([DX_CURRENT_FEATURE], [$1])
+    AC_DEFUN([DX_FLAG_DX_CURRENT_FEATURE], [DX_FLAG_$1])
+    AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2])
+    AC_ARG_ENABLE(doxygen-$1,
+                  [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1],
+                                                      [--enable-doxygen-$1]),
+                                  DX_IF_FEATURE([$1], [don't $2], [$2]))],
+                  [
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    AC_SUBST([DX_FLAG_$1], 1)
+    $3
+;; #(
+n|N|no|No|NO)
+    AC_SUBST([DX_FLAG_$1], 0)
+;; #(
+*)
+    AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1])
+;;
+esac
+], [
+AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)])
+$4
+])
+if DX_TEST_FEATURE([$1]); then
+    $5
+    :
+fi
+if DX_TEST_FEATURE([$1]); then
+    AM_CONDITIONAL(DX_COND_$1, :)
+    $6
+    :
+else
+    AM_CONDITIONAL(DX_COND_$1, false)
+    $7
+    :
+fi
+])
+
+## -------------- ##
+## Public macros. ##
+## -------------- ##
+
+# DX_XXX_FEATURE(DEFAULT_STATE)
+# -----------------------------
+AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc],  [$1])])
+AC_DEFUN([DX_MAN_FEATURE],     [AC_DEFUN([DX_FEATURE_man],  [$1])])
+AC_DEFUN([DX_HTML_FEATURE],    [AC_DEFUN([DX_FEATURE_html], [$1])])
+AC_DEFUN([DX_CHM_FEATURE],     [AC_DEFUN([DX_FEATURE_chm],  [$1])])
+AC_DEFUN([DX_CHI_FEATURE],     [AC_DEFUN([DX_FEATURE_chi],  [$1])])
+AC_DEFUN([DX_RTF_FEATURE],     [AC_DEFUN([DX_FEATURE_rtf],  [$1])])
+AC_DEFUN([DX_XML_FEATURE],     [AC_DEFUN([DX_FEATURE_xml],  [$1])])
+AC_DEFUN([DX_XML_FEATURE],     [AC_DEFUN([DX_FEATURE_xml],  [$1])])
+AC_DEFUN([DX_PDF_FEATURE],     [AC_DEFUN([DX_FEATURE_pdf],  [$1])])
+AC_DEFUN([DX_PS_FEATURE],      [AC_DEFUN([DX_FEATURE_ps],   [$1])])
+
+# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR])
+# ---------------------------------------------------------
+# PROJECT also serves as the base name for the documentation files.
+# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc".
+AC_DEFUN([DX_INIT_DOXYGEN], [
+
+# Files:
+AC_SUBST([DX_PROJECT], [$1])
+AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])])
+AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])])
+
+# Environment variables used inside doxygen.cfg:
+DX_ENV_APPEND(SRCDIR, $srcdir)
+DX_ENV_APPEND(PROJECT, $DX_PROJECT)
+DX_ENV_APPEND(DOCDIR, $DX_DOCDIR)
+DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
+
+# Doxygen itself:
+DX_ARG_ABLE(doc, [generate any doxygen documentation],
+            [],
+            [],
+            [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen)
+             DX_REQUIRE_PROG([DX_PERL], perl)],
+            [DX_ENV_APPEND(PERL_PATH, $DX_PERL)])
+
+# Dot for graphics:
+DX_ARG_ABLE(dot, [generate graphics for doxygen documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_DOT], dot)],
+            [DX_ENV_APPEND(HAVE_DOT, YES)
+             DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])],
+            [DX_ENV_APPEND(HAVE_DOT, NO)])
+
+# Man pages generation:
+DX_ARG_ABLE(man, [generate doxygen manual pages],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_MAN, YES)],
+            [DX_ENV_APPEND(GENERATE_MAN, NO)])
+
+# RTF file generation:
+DX_ARG_ABLE(rtf, [generate doxygen RTF documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_RTF, YES)],
+            [DX_ENV_APPEND(GENERATE_RTF, NO)])
+
+# XML file generation:
+DX_ARG_ABLE(xml, [generate doxygen XML documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_XML, YES)],
+            [DX_ENV_APPEND(GENERATE_XML, NO)])
+
+# (Compressed) HTML help generation:
+DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_HHC], hhc)],
+            [DX_ENV_APPEND(HHC_PATH, $DX_HHC)
+             DX_ENV_APPEND(GENERATE_HTML, YES)
+             DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
+            [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
+
+# Seperate CHI file generation.
+DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file],
+            [DX_CHECK_DEPEND(chm, 1)],
+            [DX_CLEAR_DEPEND(chm, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_CHI, YES)],
+            [DX_ENV_APPEND(GENERATE_CHI, NO)])
+
+# Plain HTML pages generation:
+DX_ARG_ABLE(html, [generate doxygen plain HTML documentation],
+            [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)],
+            [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)],
+            [],
+            [DX_ENV_APPEND(GENERATE_HTML, YES)],
+            [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)])
+
+# PostScript file generation:
+DX_ARG_ABLE(ps, [generate doxygen PostScript documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_LATEX], latex)
+             DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+             DX_REQUIRE_PROG([DX_DVIPS], dvips)
+             DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# PDF file generation:
+DX_ARG_ABLE(pdf, [generate doxygen PDF documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex)
+             DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+             DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# LaTeX generation for PS and/or PDF:
+if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then
+    AM_CONDITIONAL(DX_COND_latex, :)
+    DX_ENV_APPEND(GENERATE_LATEX, YES)
+else
+    AM_CONDITIONAL(DX_COND_latex, false)
+    DX_ENV_APPEND(GENERATE_LATEX, NO)
+fi
+
+# Paper size for PS and/or PDF:
+AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
+           [a4wide (default), a4, letter, legal or executive])
+case "$DOXYGEN_PAPER_SIZE" in
+#(
+"")
+    AC_SUBST(DOXYGEN_PAPER_SIZE, "")
+;; #(
+a4wide|a4|letter|legal|executive)
+    DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
+;; #(
+*)
+    AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'])
+;;
+esac
+
+#For debugging:
+#echo DX_FLAG_doc=$DX_FLAG_doc
+#echo DX_FLAG_dot=$DX_FLAG_dot
+#echo DX_FLAG_man=$DX_FLAG_man
+#echo DX_FLAG_html=$DX_FLAG_html
+#echo DX_FLAG_chm=$DX_FLAG_chm
+#echo DX_FLAG_chi=$DX_FLAG_chi
+#echo DX_FLAG_rtf=$DX_FLAG_rtf
+#echo DX_FLAG_xml=$DX_FLAG_xml
+#echo DX_FLAG_pdf=$DX_FLAG_pdf
+#echo DX_FLAG_ps=$DX_FLAG_ps
+#echo DX_ENV=$DX_ENV
+])
diff --git a/aclocal.m4 b/aclocal.m4
index 866c84a..b32c00c 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -47,7 +47,8 @@ To do so, use the procedure documented by the package, typically `autoreconf'.])
 # ----------------------------------
 AC_DEFUN([PKG_PROG_PKG_CONFIG],
 [m4_pattern_forbid([^_?PKG_[A-Z_]+$])
-m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
 AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
 AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
 AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
@@ -93,7 +94,8 @@ m4_define([_PKG_CONFIG],
     pkg_cv_[]$1="$$1"
  elif test -n "$PKG_CONFIG"; then
     PKG_CHECK_EXISTS([$3],
-                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes ],
 		     [pkg_failed=yes])
  else
     pkg_failed=untried
@@ -141,9 +143,9 @@ if test $pkg_failed = yes; then
    	AC_MSG_RESULT([no])
         _PKG_SHORT_ERRORS_SUPPORTED
         if test $_pkg_short_errors_supported = yes; then
-	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1`
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
         else 
-	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1`
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
@@ -156,7 +158,7 @@ $$1_PKG_ERRORS
 Consider adjusting the PKG_CONFIG_PATH environment variable if you
 installed software in a non-standard prefix.
 
-_PKG_TEXT])
+_PKG_TEXT])[]dnl
         ])
 elif test $pkg_failed = untried; then
      	AC_MSG_RESULT([no])
@@ -167,7 +169,7 @@ path to pkg-config.
 
 _PKG_TEXT
 
-To get pkg-config, see <http://pkg-config.freedesktop.org/>.])
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
         ])
 else
 	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
@@ -1107,3 +1109,4 @@ AC_SUBST([am__tar])
 AC_SUBST([am__untar])
 ]) # _AM_PROG_TAR
 
+m4_include([acinclude.m4])
diff --git a/aminclude.am b/aminclude.am
new file mode 100644
index 0000000..420049e
--- /dev/null
+++ b/aminclude.am
@@ -0,0 +1,186 @@
+# Copyright (C) 2004 Oren Ben-Kiki
+# This file is distributed under the same terms as the Automake macro files.
+
+# Generate automatic documentation using Doxygen. Goals and variables values
+# are controlled by the various DX_COND_??? conditionals set by autoconf.
+#
+# The provided goals are:
+# doxygen-doc: Generate all doxygen documentation.
+# doxygen-run: Run doxygen, which will generate some of the documentation
+#              (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post
+#              processing required for the rest of it (PS, PDF, and some MAN).
+# doxygen-man: Rename some doxygen generated man pages.
+# doxygen-ps: Generate doxygen PostScript documentation.
+# doxygen-pdf: Generate doxygen PDF documentation.
+#
+# Note that by default these are not integrated into the automake goals. If
+# doxygen is used to generate man pages, you can achieve this integration by
+# setting man3_MANS to the list of man pages generated and then adding the
+# dependency:
+#
+#   $(man3_MANS): doxygen-doc
+#
+# This will cause make to run doxygen and generate all the documentation.
+#
+# The following variable is intended for use in Makefile.am:
+#
+# DX_CLEANFILES = everything to clean.
+#
+# This is usually added to MOSTLYCLEANFILES.
+
+## --------------------------------- ##
+## Format-independent Doxygen rules. ##
+## --------------------------------- ##
+
+if DX_COND_doc
+
+## ------------------------------- ##
+## Rules specific for HTML output. ##
+## ------------------------------- ##
+
+if DX_COND_html
+
+DX_CLEAN_HTML = @DX_DOCDIR@/html
+
+endif DX_COND_html
+
+## ------------------------------ ##
+## Rules specific for CHM output. ##
+## ------------------------------ ##
+
+if DX_COND_chm
+
+DX_CLEAN_CHM = @DX_DOCDIR@/chm
+
+if DX_COND_chi
+
+DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE at .chi
+
+endif DX_COND_chi
+
+endif DX_COND_chm
+
+## ------------------------------ ##
+## Rules specific for MAN output. ##
+## ------------------------------ ##
+
+if DX_COND_man
+
+DX_CLEAN_MAN = @DX_DOCDIR@/man
+
+endif DX_COND_man
+
+## ------------------------------ ##
+## Rules specific for RTF output. ##
+## ------------------------------ ##
+
+if DX_COND_rtf
+
+DX_CLEAN_RTF = @DX_DOCDIR@/rtf
+
+endif DX_COND_rtf
+
+## ------------------------------ ##
+## Rules specific for XML output. ##
+## ------------------------------ ##
+
+if DX_COND_xml
+
+DX_CLEAN_XML = @DX_DOCDIR@/xml
+
+endif DX_COND_xml
+
+## ----------------------------- ##
+## Rules specific for PS output. ##
+## ----------------------------- ##
+
+if DX_COND_ps
+
+DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE at .ps
+
+DX_PS_GOAL = doxygen-ps
+
+doxygen-ps: @DX_DOCDIR@/@PACKAGE at .ps
+
+ at DX_DOCDIR@/@PACKAGE at .ps: @DX_DOCDIR@/@PACKAGE at .tag
+	cd @DX_DOCDIR@/latex; \
+	rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+	$(DX_LATEX) refman.tex; \
+	$(MAKEINDEX_PATH) refman.idx; \
+	$(DX_LATEX) refman.tex; \
+	countdown=5; \
+	while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+	                  refman.log > /dev/null 2>&1 \
+	   && test $$countdown -gt 0; do \
+	    $(DX_LATEX) refman.tex; \
+	    countdown=`expr $$countdown - 1`; \
+	done; \
+	$(DX_DVIPS) -o ../@PACKAGE at .ps refman.dvi
+
+endif DX_COND_ps
+
+## ------------------------------ ##
+## Rules specific for PDF output. ##
+## ------------------------------ ##
+
+if DX_COND_pdf
+
+DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE at .pdf
+
+DX_PDF_GOAL = doxygen-pdf
+
+doxygen-pdf: @DX_DOCDIR@/@PACKAGE at .pdf
+
+ at DX_DOCDIR@/@PACKAGE at .pdf: @DX_DOCDIR@/@PACKAGE at .tag
+	cd @DX_DOCDIR@/latex; \
+	rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \
+	$(DX_PDFLATEX) refman.tex; \
+	$(DX_MAKEINDEX) refman.idx; \
+	$(DX_PDFLATEX) refman.tex; \
+	countdown=5; \
+	while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \
+	                  refman.log > /dev/null 2>&1 \
+	   && test $$countdown -gt 0; do \
+	    $(DX_PDFLATEX) refman.tex; \
+	    countdown=`expr $$countdown - 1`; \
+	done; \
+	mv refman.pdf ../@PACKAGE at .pdf
+
+endif DX_COND_pdf
+
+## ------------------------------------------------- ##
+## Rules specific for LaTeX (shared for PS and PDF). ##
+## ------------------------------------------------- ##
+
+if DX_COND_latex
+
+DX_CLEAN_LATEX = @DX_DOCDIR@/latex
+
+endif DX_COND_latex
+
+.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+doxygen-run: @DX_DOCDIR@/@PACKAGE at .tag
+
+doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL)
+
+ at DX_DOCDIR@/@PACKAGE at .tag: $(DX_CONFIG) $(pkginclude_HEADERS)
+	rm -rf @DX_DOCDIR@
+	$(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG)
+
+DX_CLEANFILES = \
+    @DX_DOCDIR@/@PACKAGE at .tag \
+    -r \
+    $(DX_CLEAN_HTML) \
+    $(DX_CLEAN_CHM) \
+    $(DX_CLEAN_CHI) \
+    $(DX_CLEAN_MAN) \
+    $(DX_CLEAN_RTF) \
+    $(DX_CLEAN_XML) \
+    $(DX_CLEAN_PS) \
+    $(DX_CLEAN_PDF) \
+    $(DX_CLEAN_LATEX)
+
+endif DX_COND_doc
diff --git a/configure b/configure
index e195c2b..394b8b9 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for qmidiarp 0.3.9.
+# Generated by GNU Autoconf 2.68 for qmidiarp 0.4.1.
 #
 # Report bugs to <qmidiarp-devel at lists.sourceforge.net>.
 #
@@ -560,8 +560,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='qmidiarp'
 PACKAGE_TARNAME='qmidiarp'
-PACKAGE_VERSION='0.3.9'
-PACKAGE_STRING='qmidiarp 0.3.9'
+PACKAGE_VERSION='0.4.1'
+PACKAGE_STRING='qmidiarp 0.4.1'
 PACKAGE_BUGREPORT='qmidiarp-devel at lists.sourceforge.net'
 PACKAGE_URL=''
 
@@ -642,6 +642,53 @@ CPPFLAGS
 LDFLAGS
 CXXFLAGS
 CXX
+DOXYGEN_PAPER_SIZE
+DX_COND_latex_FALSE
+DX_COND_latex_TRUE
+DX_COND_pdf_FALSE
+DX_COND_pdf_TRUE
+DX_PDFLATEX
+DX_FLAG_pdf
+DX_COND_ps_FALSE
+DX_COND_ps_TRUE
+DX_EGREP
+DX_DVIPS
+DX_MAKEINDEX
+DX_LATEX
+DX_FLAG_ps
+DX_COND_html_FALSE
+DX_COND_html_TRUE
+DX_FLAG_html
+DX_COND_chi_FALSE
+DX_COND_chi_TRUE
+DX_FLAG_chi
+DX_COND_chm_FALSE
+DX_COND_chm_TRUE
+DX_HHC
+DX_FLAG_chm
+DX_COND_xml_FALSE
+DX_COND_xml_TRUE
+DX_FLAG_xml
+DX_COND_rtf_FALSE
+DX_COND_rtf_TRUE
+DX_FLAG_rtf
+DX_COND_man_FALSE
+DX_COND_man_TRUE
+DX_FLAG_man
+DX_COND_dot_FALSE
+DX_COND_dot_TRUE
+DX_DOT
+DX_FLAG_dot
+DX_COND_doc_FALSE
+DX_COND_doc_TRUE
+DX_PERL
+DX_FLAG_DX_CURRENT_FEATURE
+DX_DOXYGEN
+DX_FLAG_doc
+DX_ENV
+DX_DOCDIR
+DX_CONFIG
+DX_PROJECT
 am__untar
 am__tar
 AMTAR
@@ -706,11 +753,22 @@ SHELL'
 ac_subst_files=''
 ac_user_opts='
 enable_option_checking
+enable_doxygen_doc
+enable_doxygen_dot
+enable_doxygen_man
+enable_doxygen_rtf
+enable_doxygen_xml
+enable_doxygen_chm
+enable_doxygen_chi
+enable_doxygen_html
+enable_doxygen_ps
+enable_doxygen_pdf
 enable_dependency_tracking
 '
       ac_precious_vars='build_alias
 host_alias
 target_alias
+DOXYGEN_PAPER_SIZE
 CXX
 CXXFLAGS
 LDFLAGS
@@ -1267,7 +1325,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures qmidiarp 0.3.9 to adapt to many kinds of systems.
+\`configure' configures qmidiarp 0.4.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1333,7 +1391,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of qmidiarp 0.3.9:";;
+     short | recursive ) echo "Configuration of qmidiarp 0.4.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1341,10 +1399,23 @@ Optional Features:
   --disable-option-checking  ignore unrecognized --enable/--with options
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --disable-doxygen-doc   don't generate any doxygen documentation
+  --disable-doxygen-dot   don't generate graphics for doxygen documentation
+  --enable-doxygen-man    generate doxygen manual pages
+  --enable-doxygen-rtf    generate doxygen RTF documentation
+  --enable-doxygen-xml    generate doxygen XML documentation
+  --enable-doxygen-chm    generate doxygen compressed HTML help documentation
+  --enable-doxygen-chi    generate doxygen seperate compressed HTML help index
+                          file
+  --disable-doxygen-html  don't generate doxygen plain HTML documentation
+  --enable-doxygen-ps     generate doxygen PostScript documentation
+  --enable-doxygen-pdf    generate doxygen PDF documentation
   --disable-dependency-tracking  speeds up one-time build
   --enable-dependency-tracking   do not reject slow dependency extractors
 
 Some influential environment variables:
+  DOXYGEN_PAPER_SIZE
+              a4wide (default), a4, letter, legal or executive
   CXX         C++ compiler command
   CXXFLAGS    C++ compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
@@ -1429,7 +1500,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-qmidiarp configure 0.3.9
+qmidiarp configure 0.4.1
 generated by GNU Autoconf 2.68
 
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1890,7 +1961,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by qmidiarp $as_me 0.3.9, which was
+It was created by qmidiarp $as_me 0.4.1, which was
 generated by GNU Autoconf 2.68.  Invocation command line was
 
   $ $0 $@
@@ -2708,7 +2779,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='qmidiarp'
- VERSION='0.3.9'
+ VERSION='0.4.1'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -2752,6 +2823,1943 @@ am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
 $as_echo "#define APP_NAME \"QMidiArp\"" >>confdefs.h
 
 
+# Initialize doxygen
+
+
+
+
+
+
+
+
+
+
+# Files:
+DX_PROJECT=$PACKAGE_NAME
+
+DX_CONFIG=Doxyfile
+
+DX_DOCDIR=doxygen-doc
+
+
+# Environment variables used inside doxygen.cfg:
+DX_ENV="$DX_ENV SRCDIR='$srcdir'"
+
+DX_ENV="$DX_ENV PROJECT='$DX_PROJECT'"
+
+DX_ENV="$DX_ENV DOCDIR='$DX_DOCDIR'"
+
+DX_ENV="$DX_ENV VERSION='$PACKAGE_VERSION'"
+
+
+# Doxygen itself:
+
+
+
+
+    # Check whether --enable-doxygen-doc was given.
+if test "${enable_doxygen_doc+set}" = set; then :
+  enableval=$enable_doxygen_doc;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_doc=1
+
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_doc=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-doc" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_doc=1
+
+
+
+fi
+
+if test "$DX_FLAG_doc" = 1; then
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}doxygen", so it can be a program name with args.
+set dummy ${ac_tool_prefix}doxygen; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_DOXYGEN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_DOXYGEN in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_DOXYGEN="$DX_DOXYGEN" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_DOXYGEN="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_DOXYGEN=$ac_cv_path_DX_DOXYGEN
+if test -n "$DX_DOXYGEN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_DOXYGEN" >&5
+$as_echo "$DX_DOXYGEN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_DOXYGEN"; then
+  ac_pt_DX_DOXYGEN=$DX_DOXYGEN
+  # Extract the first word of "doxygen", so it can be a program name with args.
+set dummy doxygen; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_DOXYGEN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_DOXYGEN in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_DOXYGEN="$ac_pt_DX_DOXYGEN" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_DOXYGEN="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_DOXYGEN=$ac_cv_path_ac_pt_DX_DOXYGEN
+if test -n "$ac_pt_DX_DOXYGEN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DOXYGEN" >&5
+$as_echo "$ac_pt_DX_DOXYGEN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_DOXYGEN" = x; then
+    DX_DOXYGEN=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_DOXYGEN=$ac_pt_DX_DOXYGEN
+  fi
+else
+  DX_DOXYGEN="$ac_cv_path_DX_DOXYGEN"
+fi
+
+if test "$DX_FLAG_doc$DX_DOXYGEN" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: doxygen not found - will not generate any doxygen documentation" >&5
+$as_echo "$as_me: WARNING: doxygen not found - will not generate any doxygen documentation" >&2;}
+    DX_FLAG_doc=0
+
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}perl", so it can be a program name with args.
+set dummy ${ac_tool_prefix}perl; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_PERL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_PERL in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_PERL="$DX_PERL" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_PERL="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_PERL=$ac_cv_path_DX_PERL
+if test -n "$DX_PERL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_PERL" >&5
+$as_echo "$DX_PERL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_PERL"; then
+  ac_pt_DX_PERL=$DX_PERL
+  # Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_PERL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_PERL in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_PERL="$ac_pt_DX_PERL" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_PERL="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_PERL=$ac_cv_path_ac_pt_DX_PERL
+if test -n "$ac_pt_DX_PERL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_PERL" >&5
+$as_echo "$ac_pt_DX_PERL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_PERL" = x; then
+    DX_PERL=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_PERL=$ac_pt_DX_PERL
+  fi
+else
+  DX_PERL="$ac_cv_path_DX_PERL"
+fi
+
+if test "$DX_FLAG_doc$DX_PERL" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: perl not found - will not generate any doxygen documentation" >&5
+$as_echo "$as_me: WARNING: perl not found - will not generate any doxygen documentation" >&2;}
+    DX_FLAG_doc=0
+
+fi
+
+    :
+fi
+if test "$DX_FLAG_doc" = 1; then
+     if :; then
+  DX_COND_doc_TRUE=
+  DX_COND_doc_FALSE='#'
+else
+  DX_COND_doc_TRUE='#'
+  DX_COND_doc_FALSE=
+fi
+
+    DX_ENV="$DX_ENV PERL_PATH='$DX_PERL'"
+
+    :
+else
+     if false; then
+  DX_COND_doc_TRUE=
+  DX_COND_doc_FALSE='#'
+else
+  DX_COND_doc_TRUE='#'
+  DX_COND_doc_FALSE=
+fi
+
+
+    :
+fi
+
+
+# Dot for graphics:
+
+
+
+
+    # Check whether --enable-doxygen-dot was given.
+if test "${enable_doxygen_dot+set}" = set; then :
+  enableval=$enable_doxygen_dot;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_dot=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-dot requires doxygen-dot" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_dot=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-dot" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_dot=1
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_dot=0
+
+
+
+fi
+
+if test "$DX_FLAG_dot" = 1; then
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dot", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dot; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_DOT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_DOT in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_DOT="$DX_DOT" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_DOT="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_DOT=$ac_cv_path_DX_DOT
+if test -n "$DX_DOT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_DOT" >&5
+$as_echo "$DX_DOT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_DOT"; then
+  ac_pt_DX_DOT=$DX_DOT
+  # Extract the first word of "dot", so it can be a program name with args.
+set dummy dot; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_DOT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_DOT in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_DOT="$ac_pt_DX_DOT" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_DOT="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_DOT=$ac_cv_path_ac_pt_DX_DOT
+if test -n "$ac_pt_DX_DOT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DOT" >&5
+$as_echo "$ac_pt_DX_DOT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_DOT" = x; then
+    DX_DOT=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_DOT=$ac_pt_DX_DOT
+  fi
+else
+  DX_DOT="$ac_cv_path_DX_DOT"
+fi
+
+if test "$DX_FLAG_dot$DX_DOT" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: dot not found - will not generate graphics for doxygen documentation" >&5
+$as_echo "$as_me: WARNING: dot not found - will not generate graphics for doxygen documentation" >&2;}
+    DX_FLAG_dot=0
+
+fi
+
+    :
+fi
+if test "$DX_FLAG_dot" = 1; then
+     if :; then
+  DX_COND_dot_TRUE=
+  DX_COND_dot_FALSE='#'
+else
+  DX_COND_dot_TRUE='#'
+  DX_COND_dot_FALSE=
+fi
+
+    DX_ENV="$DX_ENV HAVE_DOT='YES'"
+
+             DX_ENV="$DX_ENV DOT_PATH='`expr ".$DX_DOT" : '\(\.\)[^/]*$' \| "x$DX_DOT" : 'x\(.*\)/[^/]*$'`'"
+
+    :
+else
+     if false; then
+  DX_COND_dot_TRUE=
+  DX_COND_dot_FALSE='#'
+else
+  DX_COND_dot_TRUE='#'
+  DX_COND_dot_FALSE=
+fi
+
+    DX_ENV="$DX_ENV HAVE_DOT='NO'"
+
+    :
+fi
+
+
+# Man pages generation:
+
+
+
+
+    # Check whether --enable-doxygen-man was given.
+if test "${enable_doxygen_man+set}" = set; then :
+  enableval=$enable_doxygen_man;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_man=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-man requires doxygen-man" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_man=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-man" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_man=0
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_man=0
+
+
+
+fi
+
+if test "$DX_FLAG_man" = 1; then
+
+    :
+fi
+if test "$DX_FLAG_man" = 1; then
+     if :; then
+  DX_COND_man_TRUE=
+  DX_COND_man_FALSE='#'
+else
+  DX_COND_man_TRUE='#'
+  DX_COND_man_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_MAN='YES'"
+
+    :
+else
+     if false; then
+  DX_COND_man_TRUE=
+  DX_COND_man_FALSE='#'
+else
+  DX_COND_man_TRUE='#'
+  DX_COND_man_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_MAN='NO'"
+
+    :
+fi
+
+
+# RTF file generation:
+
+
+
+
+    # Check whether --enable-doxygen-rtf was given.
+if test "${enable_doxygen_rtf+set}" = set; then :
+  enableval=$enable_doxygen_rtf;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_rtf=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-rtf requires doxygen-rtf" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_rtf=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-rtf" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_rtf=0
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_rtf=0
+
+
+
+fi
+
+if test "$DX_FLAG_rtf" = 1; then
+
+    :
+fi
+if test "$DX_FLAG_rtf" = 1; then
+     if :; then
+  DX_COND_rtf_TRUE=
+  DX_COND_rtf_FALSE='#'
+else
+  DX_COND_rtf_TRUE='#'
+  DX_COND_rtf_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_RTF='YES'"
+
+    :
+else
+     if false; then
+  DX_COND_rtf_TRUE=
+  DX_COND_rtf_FALSE='#'
+else
+  DX_COND_rtf_TRUE='#'
+  DX_COND_rtf_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_RTF='NO'"
+
+    :
+fi
+
+
+# XML file generation:
+
+
+
+
+    # Check whether --enable-doxygen-xml was given.
+if test "${enable_doxygen_xml+set}" = set; then :
+  enableval=$enable_doxygen_xml;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_xml=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-xml requires doxygen-xml" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_xml=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-xml" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_xml=0
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_xml=0
+
+
+
+fi
+
+if test "$DX_FLAG_xml" = 1; then
+
+    :
+fi
+if test "$DX_FLAG_xml" = 1; then
+     if :; then
+  DX_COND_xml_TRUE=
+  DX_COND_xml_FALSE='#'
+else
+  DX_COND_xml_TRUE='#'
+  DX_COND_xml_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_XML='YES'"
+
+    :
+else
+     if false; then
+  DX_COND_xml_TRUE=
+  DX_COND_xml_FALSE='#'
+else
+  DX_COND_xml_TRUE='#'
+  DX_COND_xml_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_XML='NO'"
+
+    :
+fi
+
+
+# (Compressed) HTML help generation:
+
+
+
+
+    # Check whether --enable-doxygen-chm was given.
+if test "${enable_doxygen_chm+set}" = set; then :
+  enableval=$enable_doxygen_chm;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_chm=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-chm requires doxygen-chm" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_chm=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-chm" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_chm=0
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_chm=0
+
+
+
+fi
+
+if test "$DX_FLAG_chm" = 1; then
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}hhc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}hhc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_HHC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_HHC in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_HHC="$DX_HHC" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_HHC="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_HHC=$ac_cv_path_DX_HHC
+if test -n "$DX_HHC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_HHC" >&5
+$as_echo "$DX_HHC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_HHC"; then
+  ac_pt_DX_HHC=$DX_HHC
+  # Extract the first word of "hhc", so it can be a program name with args.
+set dummy hhc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_HHC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_HHC in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_HHC="$ac_pt_DX_HHC" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_HHC="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_HHC=$ac_cv_path_ac_pt_DX_HHC
+if test -n "$ac_pt_DX_HHC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_HHC" >&5
+$as_echo "$ac_pt_DX_HHC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_HHC" = x; then
+    DX_HHC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_HHC=$ac_pt_DX_HHC
+  fi
+else
+  DX_HHC="$ac_cv_path_DX_HHC"
+fi
+
+if test "$DX_FLAG_chm$DX_HHC" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: hhc not found - will not generate doxygen compressed HTML help documentation" >&5
+$as_echo "$as_me: WARNING: hhc not found - will not generate doxygen compressed HTML help documentation" >&2;}
+    DX_FLAG_chm=0
+
+fi
+
+    :
+fi
+if test "$DX_FLAG_chm" = 1; then
+     if :; then
+  DX_COND_chm_TRUE=
+  DX_COND_chm_FALSE='#'
+else
+  DX_COND_chm_TRUE='#'
+  DX_COND_chm_FALSE=
+fi
+
+    DX_ENV="$DX_ENV HHC_PATH='$DX_HHC'"
+
+             DX_ENV="$DX_ENV GENERATE_HTML='YES'"
+
+             DX_ENV="$DX_ENV GENERATE_HTMLHELP='YES'"
+
+    :
+else
+     if false; then
+  DX_COND_chm_TRUE=
+  DX_COND_chm_FALSE='#'
+else
+  DX_COND_chm_TRUE='#'
+  DX_COND_chm_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_HTMLHELP='NO'"
+
+    :
+fi
+
+
+# Seperate CHI file generation.
+
+
+
+
+    # Check whether --enable-doxygen-chi was given.
+if test "${enable_doxygen_chi+set}" = set; then :
+  enableval=$enable_doxygen_chi;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_chi=1
+
+
+test "$DX_FLAG_chm" = "1" \
+|| as_fn_error $? "doxygen-chi requires doxygen-chi" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_chi=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-chi" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_chi=0
+
+
+test "$DX_FLAG_chm" = "1" || DX_FLAG_chi=0
+
+
+
+fi
+
+if test "$DX_FLAG_chi" = 1; then
+
+    :
+fi
+if test "$DX_FLAG_chi" = 1; then
+     if :; then
+  DX_COND_chi_TRUE=
+  DX_COND_chi_FALSE='#'
+else
+  DX_COND_chi_TRUE='#'
+  DX_COND_chi_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_CHI='YES'"
+
+    :
+else
+     if false; then
+  DX_COND_chi_TRUE=
+  DX_COND_chi_FALSE='#'
+else
+  DX_COND_chi_TRUE='#'
+  DX_COND_chi_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_CHI='NO'"
+
+    :
+fi
+
+
+# Plain HTML pages generation:
+
+
+
+
+    # Check whether --enable-doxygen-html was given.
+if test "${enable_doxygen_html+set}" = set; then :
+  enableval=$enable_doxygen_html;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_html=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-html requires doxygen-html" "$LINENO" 5
+
+test "$DX_FLAG_chm" = "0" \
+|| as_fn_error $? "doxygen-html contradicts doxygen-html" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_html=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-html" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_html=1
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_html=0
+
+
+test "$DX_FLAG_chm" = "0" || DX_FLAG_html=0
+
+
+
+fi
+
+if test "$DX_FLAG_html" = 1; then
+
+    :
+fi
+if test "$DX_FLAG_html" = 1; then
+     if :; then
+  DX_COND_html_TRUE=
+  DX_COND_html_FALSE='#'
+else
+  DX_COND_html_TRUE='#'
+  DX_COND_html_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_HTML='YES'"
+
+    :
+else
+     if false; then
+  DX_COND_html_TRUE=
+  DX_COND_html_FALSE='#'
+else
+  DX_COND_html_TRUE='#'
+  DX_COND_html_FALSE=
+fi
+
+    test "$DX_FLAG_chm" = 1 || DX_ENV="$DX_ENV GENERATE_HTML='NO'"
+
+    :
+fi
+
+
+# PostScript file generation:
+
+
+
+
+    # Check whether --enable-doxygen-ps was given.
+if test "${enable_doxygen_ps+set}" = set; then :
+  enableval=$enable_doxygen_ps;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_ps=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-ps requires doxygen-ps" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_ps=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-ps" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_ps=0
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_ps=0
+
+
+
+fi
+
+if test "$DX_FLAG_ps" = 1; then
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}latex", so it can be a program name with args.
+set dummy ${ac_tool_prefix}latex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_LATEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_LATEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_LATEX="$DX_LATEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_LATEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_LATEX=$ac_cv_path_DX_LATEX
+if test -n "$DX_LATEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_LATEX" >&5
+$as_echo "$DX_LATEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_LATEX"; then
+  ac_pt_DX_LATEX=$DX_LATEX
+  # Extract the first word of "latex", so it can be a program name with args.
+set dummy latex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_LATEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_LATEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_LATEX="$ac_pt_DX_LATEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_LATEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_LATEX=$ac_cv_path_ac_pt_DX_LATEX
+if test -n "$ac_pt_DX_LATEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_LATEX" >&5
+$as_echo "$ac_pt_DX_LATEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_LATEX" = x; then
+    DX_LATEX=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_LATEX=$ac_pt_DX_LATEX
+  fi
+else
+  DX_LATEX="$ac_cv_path_DX_LATEX"
+fi
+
+if test "$DX_FLAG_ps$DX_LATEX" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: latex not found - will not generate doxygen PostScript documentation" >&5
+$as_echo "$as_me: WARNING: latex not found - will not generate doxygen PostScript documentation" >&2;}
+    DX_FLAG_ps=0
+
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}makeindex", so it can be a program name with args.
+set dummy ${ac_tool_prefix}makeindex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_MAKEINDEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_MAKEINDEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_MAKEINDEX="$DX_MAKEINDEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_MAKEINDEX=$ac_cv_path_DX_MAKEINDEX
+if test -n "$DX_MAKEINDEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_MAKEINDEX" >&5
+$as_echo "$DX_MAKEINDEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_MAKEINDEX"; then
+  ac_pt_DX_MAKEINDEX=$DX_MAKEINDEX
+  # Extract the first word of "makeindex", so it can be a program name with args.
+set dummy makeindex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_MAKEINDEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_MAKEINDEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_MAKEINDEX="$ac_pt_DX_MAKEINDEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_MAKEINDEX=$ac_cv_path_ac_pt_DX_MAKEINDEX
+if test -n "$ac_pt_DX_MAKEINDEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_MAKEINDEX" >&5
+$as_echo "$ac_pt_DX_MAKEINDEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_MAKEINDEX" = x; then
+    DX_MAKEINDEX=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_MAKEINDEX=$ac_pt_DX_MAKEINDEX
+  fi
+else
+  DX_MAKEINDEX="$ac_cv_path_DX_MAKEINDEX"
+fi
+
+if test "$DX_FLAG_ps$DX_MAKEINDEX" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: makeindex not found - will not generate doxygen PostScript documentation" >&5
+$as_echo "$as_me: WARNING: makeindex not found - will not generate doxygen PostScript documentation" >&2;}
+    DX_FLAG_ps=0
+
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dvips", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dvips; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_DVIPS+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_DVIPS in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_DVIPS="$DX_DVIPS" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_DVIPS="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_DVIPS=$ac_cv_path_DX_DVIPS
+if test -n "$DX_DVIPS"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_DVIPS" >&5
+$as_echo "$DX_DVIPS" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_DVIPS"; then
+  ac_pt_DX_DVIPS=$DX_DVIPS
+  # Extract the first word of "dvips", so it can be a program name with args.
+set dummy dvips; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_DVIPS+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_DVIPS in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_DVIPS="$ac_pt_DX_DVIPS" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_DVIPS="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_DVIPS=$ac_cv_path_ac_pt_DX_DVIPS
+if test -n "$ac_pt_DX_DVIPS"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_DVIPS" >&5
+$as_echo "$ac_pt_DX_DVIPS" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_DVIPS" = x; then
+    DX_DVIPS=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_DVIPS=$ac_pt_DX_DVIPS
+  fi
+else
+  DX_DVIPS="$ac_cv_path_DX_DVIPS"
+fi
+
+if test "$DX_FLAG_ps$DX_DVIPS" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: dvips not found - will not generate doxygen PostScript documentation" >&5
+$as_echo "$as_me: WARNING: dvips not found - will not generate doxygen PostScript documentation" >&2;}
+    DX_FLAG_ps=0
+
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}egrep", so it can be a program name with args.
+set dummy ${ac_tool_prefix}egrep; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_EGREP in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_EGREP="$DX_EGREP" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_EGREP="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_EGREP=$ac_cv_path_DX_EGREP
+if test -n "$DX_EGREP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_EGREP" >&5
+$as_echo "$DX_EGREP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_EGREP"; then
+  ac_pt_DX_EGREP=$DX_EGREP
+  # Extract the first word of "egrep", so it can be a program name with args.
+set dummy egrep; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_EGREP in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_EGREP="$ac_pt_DX_EGREP" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_EGREP="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_EGREP=$ac_cv_path_ac_pt_DX_EGREP
+if test -n "$ac_pt_DX_EGREP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_EGREP" >&5
+$as_echo "$ac_pt_DX_EGREP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_EGREP" = x; then
+    DX_EGREP=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_EGREP=$ac_pt_DX_EGREP
+  fi
+else
+  DX_EGREP="$ac_cv_path_DX_EGREP"
+fi
+
+if test "$DX_FLAG_ps$DX_EGREP" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: egrep not found - will not generate doxygen PostScript documentation" >&5
+$as_echo "$as_me: WARNING: egrep not found - will not generate doxygen PostScript documentation" >&2;}
+    DX_FLAG_ps=0
+
+fi
+
+    :
+fi
+if test "$DX_FLAG_ps" = 1; then
+     if :; then
+  DX_COND_ps_TRUE=
+  DX_COND_ps_FALSE='#'
+else
+  DX_COND_ps_TRUE='#'
+  DX_COND_ps_FALSE=
+fi
+
+
+    :
+else
+     if false; then
+  DX_COND_ps_TRUE=
+  DX_COND_ps_FALSE='#'
+else
+  DX_COND_ps_TRUE='#'
+  DX_COND_ps_FALSE=
+fi
+
+
+    :
+fi
+
+
+# PDF file generation:
+
+
+
+
+    # Check whether --enable-doxygen-pdf was given.
+if test "${enable_doxygen_pdf+set}" = set; then :
+  enableval=$enable_doxygen_pdf;
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    DX_FLAG_pdf=1
+
+
+test "$DX_FLAG_doc" = "1" \
+|| as_fn_error $? "doxygen-pdf requires doxygen-pdf" "$LINENO" 5
+
+;; #(
+n|N|no|No|NO)
+    DX_FLAG_pdf=0
+
+;; #(
+*)
+    as_fn_error $? "invalid value '$enableval' given to doxygen-pdf" "$LINENO" 5
+;;
+esac
+
+else
+
+DX_FLAG_pdf=0
+
+
+test "$DX_FLAG_doc" = "1" || DX_FLAG_pdf=0
+
+
+
+fi
+
+if test "$DX_FLAG_pdf" = 1; then
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pdflatex", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pdflatex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_PDFLATEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_PDFLATEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_PDFLATEX="$DX_PDFLATEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_PDFLATEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_PDFLATEX=$ac_cv_path_DX_PDFLATEX
+if test -n "$DX_PDFLATEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_PDFLATEX" >&5
+$as_echo "$DX_PDFLATEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_PDFLATEX"; then
+  ac_pt_DX_PDFLATEX=$DX_PDFLATEX
+  # Extract the first word of "pdflatex", so it can be a program name with args.
+set dummy pdflatex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_PDFLATEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_PDFLATEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_PDFLATEX="$ac_pt_DX_PDFLATEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_PDFLATEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_PDFLATEX=$ac_cv_path_ac_pt_DX_PDFLATEX
+if test -n "$ac_pt_DX_PDFLATEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_PDFLATEX" >&5
+$as_echo "$ac_pt_DX_PDFLATEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_PDFLATEX" = x; then
+    DX_PDFLATEX=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_PDFLATEX=$ac_pt_DX_PDFLATEX
+  fi
+else
+  DX_PDFLATEX="$ac_cv_path_DX_PDFLATEX"
+fi
+
+if test "$DX_FLAG_pdf$DX_PDFLATEX" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pdflatex not found - will not generate doxygen PDF documentation" >&5
+$as_echo "$as_me: WARNING: pdflatex not found - will not generate doxygen PDF documentation" >&2;}
+    DX_FLAG_pdf=0
+
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}makeindex", so it can be a program name with args.
+set dummy ${ac_tool_prefix}makeindex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_MAKEINDEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_MAKEINDEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_MAKEINDEX="$DX_MAKEINDEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_MAKEINDEX=$ac_cv_path_DX_MAKEINDEX
+if test -n "$DX_MAKEINDEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_MAKEINDEX" >&5
+$as_echo "$DX_MAKEINDEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_MAKEINDEX"; then
+  ac_pt_DX_MAKEINDEX=$DX_MAKEINDEX
+  # Extract the first word of "makeindex", so it can be a program name with args.
+set dummy makeindex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_MAKEINDEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_MAKEINDEX in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_MAKEINDEX="$ac_pt_DX_MAKEINDEX" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_MAKEINDEX="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_MAKEINDEX=$ac_cv_path_ac_pt_DX_MAKEINDEX
+if test -n "$ac_pt_DX_MAKEINDEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_MAKEINDEX" >&5
+$as_echo "$ac_pt_DX_MAKEINDEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_MAKEINDEX" = x; then
+    DX_MAKEINDEX=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_MAKEINDEX=$ac_pt_DX_MAKEINDEX
+  fi
+else
+  DX_MAKEINDEX="$ac_cv_path_DX_MAKEINDEX"
+fi
+
+if test "$DX_FLAG_pdf$DX_MAKEINDEX" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: makeindex not found - will not generate doxygen PDF documentation" >&5
+$as_echo "$as_me: WARNING: makeindex not found - will not generate doxygen PDF documentation" >&2;}
+    DX_FLAG_pdf=0
+
+fi
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}egrep", so it can be a program name with args.
+set dummy ${ac_tool_prefix}egrep; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_DX_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $DX_EGREP in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_DX_EGREP="$DX_EGREP" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_DX_EGREP="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+DX_EGREP=$ac_cv_path_DX_EGREP
+if test -n "$DX_EGREP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DX_EGREP" >&5
+$as_echo "$DX_EGREP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_DX_EGREP"; then
+  ac_pt_DX_EGREP=$DX_EGREP
+  # Extract the first word of "egrep", so it can be a program name with args.
+set dummy egrep; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_DX_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_DX_EGREP in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_DX_EGREP="$ac_pt_DX_EGREP" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_ac_pt_DX_EGREP="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_DX_EGREP=$ac_cv_path_ac_pt_DX_EGREP
+if test -n "$ac_pt_DX_EGREP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_DX_EGREP" >&5
+$as_echo "$ac_pt_DX_EGREP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_DX_EGREP" = x; then
+    DX_EGREP=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DX_EGREP=$ac_pt_DX_EGREP
+  fi
+else
+  DX_EGREP="$ac_cv_path_DX_EGREP"
+fi
+
+if test "$DX_FLAG_pdf$DX_EGREP" = 1; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: egrep not found - will not generate doxygen PDF documentation" >&5
+$as_echo "$as_me: WARNING: egrep not found - will not generate doxygen PDF documentation" >&2;}
+    DX_FLAG_pdf=0
+
+fi
+
+    :
+fi
+if test "$DX_FLAG_pdf" = 1; then
+     if :; then
+  DX_COND_pdf_TRUE=
+  DX_COND_pdf_FALSE='#'
+else
+  DX_COND_pdf_TRUE='#'
+  DX_COND_pdf_FALSE=
+fi
+
+
+    :
+else
+     if false; then
+  DX_COND_pdf_TRUE=
+  DX_COND_pdf_FALSE='#'
+else
+  DX_COND_pdf_TRUE='#'
+  DX_COND_pdf_FALSE=
+fi
+
+
+    :
+fi
+
+
+# LaTeX generation for PS and/or PDF:
+if test "$DX_FLAG_ps" = 1 || test "$DX_FLAG_pdf" = 1; then
+     if :; then
+  DX_COND_latex_TRUE=
+  DX_COND_latex_FALSE='#'
+else
+  DX_COND_latex_TRUE='#'
+  DX_COND_latex_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_LATEX='YES'"
+
+else
+     if false; then
+  DX_COND_latex_TRUE=
+  DX_COND_latex_FALSE='#'
+else
+  DX_COND_latex_TRUE='#'
+  DX_COND_latex_FALSE=
+fi
+
+    DX_ENV="$DX_ENV GENERATE_LATEX='NO'"
+
+fi
+
+# Paper size for PS and/or PDF:
+
+case "$DOXYGEN_PAPER_SIZE" in
+#(
+"")
+    DOXYGEN_PAPER_SIZE=""
+
+;; #(
+a4wide|a4|letter|legal|executive)
+    DX_ENV="$DX_ENV PAPER_SIZE='$DOXYGEN_PAPER_SIZE'"
+
+;; #(
+*)
+    as_fn_error $? "unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'" "$LINENO" 5
+;;
+esac
+
+#For debugging:
+#echo DX_FLAG_doc=$DX_FLAG_doc
+#echo DX_FLAG_dot=$DX_FLAG_dot
+#echo DX_FLAG_man=$DX_FLAG_man
+#echo DX_FLAG_html=$DX_FLAG_html
+#echo DX_FLAG_chm=$DX_FLAG_chm
+#echo DX_FLAG_chi=$DX_FLAG_chi
+#echo DX_FLAG_rtf=$DX_FLAG_rtf
+#echo DX_FLAG_xml=$DX_FLAG_xml
+#echo DX_FLAG_pdf=$DX_FLAG_pdf
+#echo DX_FLAG_ps=$DX_FLAG_ps
+#echo DX_ENV=$DX_ENV
+
+
 # Checks for programs.
 ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
@@ -4377,6 +6385,7 @@ fi
 
 
 
+
 if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
 	if test -n "$ac_tool_prefix"; then
   # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
@@ -4505,6 +6514,7 @@ if test -n "$QT_CFLAGS"; then
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
   pkg_cv_QT_CFLAGS=`$PKG_CONFIG --cflags "QtCore QtGui >= 4.2.0" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
 fi
@@ -4521,6 +6531,7 @@ if test -n "$QT_LIBS"; then
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
   pkg_cv_QT_LIBS=`$PKG_CONFIG --libs "QtCore QtGui >= 4.2.0" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
 fi
@@ -4540,9 +6551,9 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        QT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "QtCore QtGui >= 4.2.0" 2>&1`
+	        QT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "QtCore QtGui >= 4.2.0" 2>&1`
         else
-	        QT_PKG_ERRORS=`$PKG_CONFIG --print-errors "QtCore QtGui >= 4.2.0" 2>&1`
+	        QT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "QtCore QtGui >= 4.2.0" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$QT_PKG_ERRORS" >&5
@@ -5762,6 +7773,94 @@ else
   am__EXEEXT_FALSE=
 fi
 
+if test -z "${DX_COND_doc_TRUE}" && test -z "${DX_COND_doc_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_doc\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_doc_TRUE}" && test -z "${DX_COND_doc_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_doc\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_dot_TRUE}" && test -z "${DX_COND_dot_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_dot\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_dot_TRUE}" && test -z "${DX_COND_dot_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_dot\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_man_TRUE}" && test -z "${DX_COND_man_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_man\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_man_TRUE}" && test -z "${DX_COND_man_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_man\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_rtf_TRUE}" && test -z "${DX_COND_rtf_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_rtf\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_rtf_TRUE}" && test -z "${DX_COND_rtf_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_rtf\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_xml_TRUE}" && test -z "${DX_COND_xml_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_xml\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_xml_TRUE}" && test -z "${DX_COND_xml_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_xml\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_chm_TRUE}" && test -z "${DX_COND_chm_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_chm\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_chm_TRUE}" && test -z "${DX_COND_chm_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_chm\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_chi_TRUE}" && test -z "${DX_COND_chi_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_chi\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_chi_TRUE}" && test -z "${DX_COND_chi_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_chi\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_html_TRUE}" && test -z "${DX_COND_html_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_html\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_html_TRUE}" && test -z "${DX_COND_html_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_html\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_ps_TRUE}" && test -z "${DX_COND_ps_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_ps\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_ps_TRUE}" && test -z "${DX_COND_ps_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_ps\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_pdf_TRUE}" && test -z "${DX_COND_pdf_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_pdf\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_pdf_TRUE}" && test -z "${DX_COND_pdf_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_pdf\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_latex_TRUE}" && test -z "${DX_COND_latex_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_latex\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DX_COND_latex_TRUE}" && test -z "${DX_COND_latex_FALSE}"; then
+  as_fn_error $? "conditional \"DX_COND_latex\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
   as_fn_error $? "conditional \"AMDEP\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -6183,7 +8282,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by qmidiarp $as_me 0.3.9, which was
+This file was extended by qmidiarp $as_me 0.4.1, which was
 generated by GNU Autoconf 2.68.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6249,7 +8348,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-qmidiarp config.status 0.3.9
+qmidiarp config.status 0.4.1
 configured by $0, generated by GNU Autoconf 2.68,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 6061afa..fb78068 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,12 +2,23 @@
 # Process this file with autoconf to produce a configure script.
 
 dnl AC_PREREQ([2.63])
-AC_INIT([qmidiarp], [0.3.9], [qmidiarp-devel at lists.sourceforge.net])
+AC_INIT([qmidiarp], [0.4.1], [qmidiarp-devel at lists.sourceforge.net])
 AC_CONFIG_SRCDIR([src/main.cpp])
 AC_CONFIG_HEADERS(src/config.h)
 AM_INIT_AUTOMAKE([dist-bzip2])
 AC_DEFINE(APP_NAME, "QMidiArp", [application name])
 
+# Initialize doxygen
+DX_HTML_FEATURE(ON)
+DX_CHM_FEATURE(OFF)
+DX_CHI_FEATURE(OFF)
+DX_MAN_FEATURE(OFF)
+DX_RTF_FEATURE(OFF)
+DX_XML_FEATURE(OFF)
+DX_PDF_FEATURE(OFF)
+DX_PS_FEATURE(OFF)
+DX_INIT_DOXYGEN($PACKAGE_NAME, Doxyfile)
+
 # Checks for programs.
 AC_PROG_CXX
 AC_PROG_CC
diff --git a/examples/Makefile.in b/examples/Makefile.in
index f272731..e579bd9 100644
--- a/examples/Makefile.in
+++ b/examples/Makefile.in
@@ -38,7 +38,8 @@ subdir = examples
 DIST_COMMON = $(dist_examplesdata_DATA) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -88,6 +89,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
diff --git a/html/qmidiarp_logo_med2.png b/html/qmidiarp_logo_med2.png
new file mode 100644
index 0000000..c18196c
Binary files /dev/null and b/html/qmidiarp_logo_med2.png differ
diff --git a/man/Makefile.in b/man/Makefile.in
index ede6aa5..2897b0a 100644
--- a/man/Makefile.in
+++ b/man/Makefile.in
@@ -37,7 +37,8 @@ subdir = man
 DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -129,6 +130,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
diff --git a/man/de/Makefile.in b/man/de/Makefile.in
index a4cf427..5bbc1dc 100644
--- a/man/de/Makefile.in
+++ b/man/de/Makefile.in
@@ -38,7 +38,8 @@ subdir = man/de
 DIST_COMMON = $(dist_deman1data_DATA) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -88,6 +89,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
diff --git a/man/de/qmidiarp.1 b/man/de/qmidiarp.1
index c4fa8cd..9405597 100644
--- a/man/de/qmidiarp.1
+++ b/man/de/qmidiarp.1
@@ -322,13 +322,19 @@ Wenn
 .B Frei
 gewählt wird, kann die Wellenform mit der
 .I linken Maustaste
-in der Wellenform-Graphik gezeichnet werden. Durch klicken auf 
-.B Kopieren in freie Wellenform
-kann eine errechnete Wellenform in die freie Form kopiert werden, um sie
-zu verändern. Dies überschreibt die vorhergehende freie Wellenform mit
+in der Wellenform-Graphik gezeichnet werden. Beim Modifizieren einer
+errechneten Wellenform wird diese automatisch in die freie Form kopiert. Dies überschreibt die vorhergehende freie Wellenform mit
 der gerade dargestellten Form. Wie alle LFO-Funktionen kann auch das 
-Zeichnen oder Stummschalten bei laufendem Sequenzer geschehen. Alle
-Änderungen werden beim nächsten ausgespielten Wellenzyklus wirkend.
+Zeichnen oder Stummschalten bei laufendem Sequenzer geschehen.
+.PP
+.B Aufnahme
+.PP
+Controller-Daten, die das Filter im Eingang passieren, können 
+kontinuierlich aufgezeichnet werden durch wählen des
+.B Aufnehmen
+Knopfes. Dieser Knopf ist selbst über MIDI ansteuerbar (siehe MIDI Learn)
+und QMidiArp wird somit zu einem einfachen Control-Looper/Sampler.
+
 .PP
 .B "LFO Ausgangs-Feld"
 .PP
@@ -338,8 +344,7 @@ Das LFO Ausgangs-Feld enthält die Einstellungen für
 und 
 .B Controller 
 ID der LFO-Daten jedes LFO-Reiters. Es erlaubt auch ein komplettes 
-Stummschalten jedes LFOs nach einem kompletten Wellenzyklus durch 
-markieren des 
+Stummschalten jedes LFOs durch Klicken des 
 .B Stumm 
 Knopfes.
 
@@ -386,8 +391,7 @@ QMidiArp erlaubt die globale Einstellung des
 .B Notenlänge
 und der
 .B Transposition
-der Sequenz in Halbtönen. Alle hier eingestellten Änderungen werden nach
-dem momentanen Durchlauf der Sequenz wirksam.
+der Sequenz in Halbtönen.
 .PP
 .B Eingans- und Ausgangsfelder der Seq Module
 .PP
@@ -403,11 +407,20 @@ die Tonhöhe der empfangenen Note transponiert. Wenn zusätzlich
 angekreuzt ist, werden auch die Anschlagsdynamik-Daten der empfangenen
 Noten auf die Sequenz übertragen, und die gesendeten Noten bekommen
 dieselbe Anschlagsdynamik wie die auf der Tastatur gespielte Note.
-Sind weder 
-.B Note
-noch
-.B Anschlag
-angekreuzt, so werden empfangene Noten ignoriert.
+Weitere Optionen des Eingangsfeldes steuern das Start- und 
+Stop-Verhalten der Sequenz beim Empfang von Noten. 
+.B Neustart
+verursacht ein Zurücksetzen der Sequenz an den Start, jedoch ohne den 
+Rhythmus zu unterbrechen. Nur wenn 
+.B Trigger
+angewählt ist, startet die Sequenz exakt mit dem Timing der angeschlagenen
+Note.
+.B Note Off
+stoppt den Notenausgang beim Loslassen von Noten. Ist
+.B Schleife
+nicht angewählt, so läuft die Sequenz nur einmal nach dem Start (oder nach)
+Notenanschlag) durch.
+
 Das
 .B Ausgangs-Feld
 der Seq-Module ist das gleiche wie das der Arpeggiator- oder LFO-Module.
@@ -556,7 +569,7 @@ geschrieben.
 .SH UNTERSTÜTZUNG
 qmidiarp-devel at lists.sourceforge.net
 .SH AUTOREN
-Matthias Nagorni, Frank Kober and Guido Scholz. Das Original dieser
+Frank Kober and Guido Scholz, Matthias Nagorni. Das Original dieser
 Handbuchseite wurde von Frank Kober <emuse at users.sourceforge.net>
 geschrieben; die deutsche Übersetzung wurde von Robert Dietrich
 <flyingrobin at online.de> angefertigt.
diff --git a/man/fr/Makefile.in b/man/fr/Makefile.in
index 27cd7aa..40af9f0 100644
--- a/man/fr/Makefile.in
+++ b/man/fr/Makefile.in
@@ -38,7 +38,8 @@ subdir = man/fr
 DIST_COMMON = $(dist_frman1data_DATA) $(srcdir)/Makefile.am \
 	$(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -88,6 +89,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
diff --git a/man/fr/qmidiarp.1 b/man/fr/qmidiarp.1
index 4f2693e..df267c4 100644
--- a/man/fr/qmidiarp.1
+++ b/man/fr/qmidiarp.1
@@ -241,7 +241,7 @@ sortie sont incrémentées pendant le temps d'attack défini en secondes.
 Si un temps "release" est spécifié, les notes relâchées sont gardées dans 
 le tampon, et leur vélocité est linéairement diminuée jusqu'à la fin du 
 temps "release". Cette fonction n'a un effet sur le son que si le 
-synthétiseur connecté à la sortie produit des sons sensible à la 
+synthétiseur connecté à la sortie produit des sons sensibles à la 
 vélocité. Elle fonctionne aux mieux avec des motifs à haute polyphonie
 comme par exemple le motif "Chord Oct 16 A".
 .PP
@@ -259,8 +259,7 @@ Les modules arpégiateurs de QMidiArp ont été inspiré par l'arpégiateur
 matériel MAP1 par Rudi Linhard.
 
 .SS "Les modules LFO"
-En parallèle aux arpégiateurs,  
-QMidiArp
+En parallèle aux arpégiateurs, QMidiArp
 peut envoyer des données de contrôle MIDI sous forme d'un oscillateur
 à basse fréquence (LFO) à une sortie spécifiée. Les données LFO sont des
 évênements MIDI envoyés en synchronie avec la queue de l'arpégiateur.
@@ -305,14 +304,20 @@ En sélectionnant la forme
 .B Libre,
 l'onde peut être dessinée ou modifiée avec le 
 .I bouton gauche 
-de la souris. Le bouton 
-.B Copier dans l'onde libre
-permet de copier la forme actuellement affichée vers la forme libre pour
-être modifiée. La forme libre précédente sera écrasée en utilisant cette
-fonction.
+de la souris. Lorqu'on tente à modifier une forme calculée, elle est
+automatiquement copiée vers la forme libre, et la forme libre précédente 
+est écrasée.
 Toutes les opérations du LFO incluant le dessin peuvent être faites 
-pendant que la queue est en route, et elles auront leur effet au 
-prochain cycle de l'onde.
+pendant que le transport est en marche.
+.PP
+.B Enregistrement
+.PP
+Les données de contrôle MIDI reçues à l'entrée peuvent être enregistrées
+en continue en appuyant sur le bouton
+.B Enregistrer.
+Ce bouton est lui-même contrôlable par MIDI (c.f. 
+.B Apprentissage MIDI)
+et QMidiArp est ainsi utilisable comme Looper et séquenceur de contrôles.
 .PP
 .B "Le champ Sortie du LFO"
 .PP
@@ -321,8 +326,8 @@ Ce champ contient les réglages du
 .B canal
 et
 .B contrôleur 
-des données produites par chaque onglet LFO. Il permet également de 
-rendre muet chaque LFO entièrement après un cycle d'onde complet.
+des données produites. Il permet également de rendre muet le module 
+entièrement.
 
 .SS "Les modules séquenceurs"
 En cliquant sur le bouton 
@@ -376,9 +381,21 @@ est cochée, la séquence est globalement transposée avec la note reçue
 comme valeur de transposition. Si la case
 .B Vélocité
 est cochée également, la séquence produira des notes avec la même
-vélocité que celle de la note reçue. Si aucun des deux cases est cochée,
-les notes reçues seront ignorées. Tous les changements de contrôle
-s'appliquent au prochain passage dans la boucle de la séquence. Le 
+vélocité que celle de la note reçue. Le champ
+.B Entrée
+définit également le comportement de déclenchement de la séquence en
+cas de réception de notes à l'entrée.
+.B Redémarre
+renvoie la séquence à son début, mais sans interrompre le rythme. La 
+séquence est re-déclenchée avec le timing des notes jouées seulement
+si
+.B Trigger
+est est coché.
+.B Note Off
+arretera la séquence avec une note relachée, et
+.B Boucle
+doit être cochée pour une répétition permanente au lieu d'un seul 
+passage. Le
 champ
 .B Sortie
 est équivalent à celui des arpégiateurs et LFOs. 
@@ -523,6 +540,6 @@ Les erreurs et avertissements sont écrits dans
 qmidiarp-devel at lists.sourceforge.net
 
 .SH AUTEURS
-Matthias Nagorni, Frank Kober et Guido Scholz. Cette page de manuel 
+Frank Kober, Guido Scholz et Matthias Nagorni. Cette page de manuel 
 a été écrite par
 Frank Kober <emuse at users.sourceforge.net>.
diff --git a/man/qmidiarp.1 b/man/qmidiarp.1
index da34a15..83f05e6 100644
--- a/man/qmidiarp.1
+++ b/man/qmidiarp.1
@@ -266,8 +266,7 @@ create swing timing and accent. The Groove settings are adjusted for all
 arps simultaneously.
 
 .SS "LFO Modules"
-In parallel to the arps, 
-QMidiArp
+In parallel to the arps, QMidiArp
 can send MIDI controller data in form of a low frequency oscillator (LFO)
 to the assigned output. The LFO data consist of controller events that 
 are in sync with the arpeggiator queue. The queue has to be in running 
@@ -279,8 +278,7 @@ panel to define MIDI Channel, ALSA port and controller number to be
 produced. The waveform can currently be set to Sine,
 Saw Up, Saw Down, Triangle, Square and Custom. The 
 .B frequency 
-of the LFO can be
-set in muliples and divisors of the arp 
+of the LFO can be set in muliples and divisors of the arp 
 .B tempo, 
 such that frequency of 1
 produces one full wave per beat. If frequencies lower than 1 are
@@ -295,8 +293,7 @@ and
 of the waveform can be adjusted from 0...127. Low 
 .B resolutions 
 lead to audibly discrete rythmic controller changes whereas higher 
-resolution values lead to 
-more continuous waves.
+resolution values lead to more continuous waves.
 .PP
 .B Muting individual wave points
 .PP
@@ -311,13 +308,21 @@ When
 .B Custom
 is selected, the waveform can be drawn with the
 .I left mouse button
-in the waveform display. A calculated waveform can be copied to the
-custom waveform by clicking on the
-.B Copy to custom
-button, which will overwrite the previous custom waveform with the 
-currently displayed waveform. As all LFO operations, drawing and muting
-can be done while the queue is running and will have effect on the next
-output wavecycle.
+in the waveform display. A calculated waveform is copied to the custom 
+waveform whenever it is being modified by the mouse. This will overwrite 
+the previous custom waveform with the currently displayed waveform. As 
+all LFO operations, drawing and muting can be done while the queue is 
+running, and becomes effective immediately.
+.PP
+.B Recording
+.PP
+The LFO records incoming controller data as selected in the 
+.B Input
+panel, when the 
+.B Record
+button is pressed. Note that the Record button itself can be attributed
+to a MIDI toggle controller so that it provides a convenient 
+implementation of a controller motion sampler and looper.
 .PP
 .B "LFO Output panel"
 .PP
@@ -369,8 +374,7 @@ There are sliders to adjust the global
 .B note length 
 and 
 .B transpose 
-of the sequence in semitones. All changes made to these controls
-apply after completion of the current loop.
+of the sequence in semitones.
 .PP
 .B Seq Input and Output panels
 .PP
@@ -384,12 +388,13 @@ is checked, the sequence will be globally transposed with the incoming
 note as transpose value. If 
 .B Velocity
 is checked in addition, the sequence will output notes with the same 
-velocity as that received on its input. If neither
-.B Note
-nor
-.B Velocity
-are checked, incoming notes will have no effect. All changes due to 
-incoming notes apply after completion of the current loop. The Seq
+velocity as that received on its input. The 
+.B Input
+panel also determines how the sequence behaves when incoming notes
+are received. It can be restarted, triggered and stopped with the 
+timing of received notes. 
+
+The Seq
 .B Output
 panel is equivalent to that of arpeggiator and LFO modules. 
 .PP 
@@ -523,6 +528,6 @@ Errors and warnings are written to
 .SH SUPPORT
 qmidiarp-devel at lists.sourceforge.net
 .SH AUTHORS
-Matthias Nagorni, Frank Kober and Guido Scholz. This
+Frank Kober, Guido Scholz and Matthias Nagorni. This
 manual page was written by
 Frank Kober <emuse at users.sourceforge.net>.
diff --git a/qmidiarp.desktop b/qmidiarp.desktop
new file mode 100644
index 0000000..9a3cc57
--- /dev/null
+++ b/qmidiarp.desktop
@@ -0,0 +1,19 @@
+[Desktop Entry]
+Encoding=UTF-8
+Type=Application
+Categories=AudioVideo;X-Sound;Midi;Audio;AudioVideoEditing;X-Jack;X-Midi;
+Name=QMidiArp
+Name[de]=QMidiArp
+Name[fr]=QMidiArp
+GenericName=Arpeggiator-Sequencer-LFO
+GenericName[de]=Arpeggiator-Sequenzer-LFO
+GenericName[fr]=Arpégiateur-Séquenceur-LFO
+Comment=Arpeggiator-Sequencer-LFO
+Comment[de]=Arpeggiator-Sequenzer-LFO
+Comment[fr]=Arpégiateur-Séquenceur-LFO
+Icon=qmidiarp
+MimeType=application/qma;application/qmax;
+Exec=qmidiarp
+TryExec=qmidiarp
+Terminal=false
+StartupNotify=true
diff --git a/qmidiarp.svg b/qmidiarp.svg
new file mode 100644
index 0000000..0dfa76e
--- /dev/null
+++ b/qmidiarp.svg
@@ -0,0 +1,332 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="128"
+   height="128"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="qmidiarp.svg">
+  <defs
+     id="defs4">
+    <filter
+       color-interpolation-filters="sRGB"
+       inkscape:collect="always"
+       id="filter3787">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.3609914"
+         id="feGaussianBlur3789" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.32"
+     inkscape:cx="54.968368"
+     inkscape:cy="82.136398"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="960"
+     inkscape:window-height="634"
+     inkscape:window-x="102"
+     inkscape:window-y="10"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-924.36218)">
+    <rect
+       style="fill:#1a1a1a;fill-opacity:0.70297032;fill-rule:nonzero;stroke:none;filter:url(#filter3787)"
+       id="rect4931"
+       width="123.27586"
+       height="58.189655"
+       x="2.1551704"
+       y="983.61212"
+       transform="matrix(0.96932332,0,0,1,2.1670552,0)" />
+    <rect
+       style="fill:#003200;fill-opacity:1;stroke:#00a440;stroke-width:0.97753412;stroke-opacity:1"
+       id="rect2985"
+       width="120.28191"
+       height="59.090908"
+       x="0.69930071"
+       y="973.60693" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 15.384615,980.59994 c 0,46.50346 0.349651,45.80416 0.349651,45.80416 l 0,0.3497"
+       id="path3759"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 22.916084,980.86567 c 0,46.50353 0.349651,45.80423 0.349651,45.80423 l 0,0.3496"
+       id="path3759-5"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 30.916084,980.86567 c 0,46.50353 0.349651,45.80423 0.349651,45.80423 l 0,0.3496"
+       id="path3759-0"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 38.916084,980.86567 c 0,46.50353 0.349651,45.80423 0.349651,45.80423 l 0,0.3496"
+       id="path3759-3"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 46.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-1"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 54.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-06"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 62.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-32"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 70.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-061"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 78.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-55"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 86.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-4"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 94.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-7"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 102.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-6"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#004f00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 110.52448,981.35519 c 0,46.50351 0.34965,45.80421 0.34965,45.80421 l 0,0.3496"
+       id="path3759-56"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:5.54682684px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00901d;fill-opacity:1;stroke:none;font-family:Sans"
+       x="19.374784"
+       y="1088.694"
+       id="text3870"
+       sodipodi:linespacing="125%"
+       transform="scale(1.112215,0.89910674)"><tspan
+         sodipodi:role="line"
+         id="tspan3872"
+         x="19.374784"
+         y="1088.694">1</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:5.54682684px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00901d;fill-opacity:1;stroke:none;font-family:Sans"
+       x="47.367229"
+       y="1088.4608"
+       id="text3870-3"
+       sodipodi:linespacing="125%"
+       transform="scale(1.112215,0.89910674)"><tspan
+         sodipodi:role="line"
+         id="tspan3872-7"
+         x="47.367229"
+         y="1088.4608">2</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:5.54682684px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00901d;fill-opacity:1;stroke:none;font-family:Sans"
+       x="76.651688"
+       y="1088.5439"
+       id="text3870-3-4"
+       sodipodi:linespacing="125%"
+       transform="scale(1.112215,0.89910674)"><tspan
+         sodipodi:role="line"
+         id="tspan3872-7-5"
+         x="76.651688"
+         y="1088.5439">3</tspan></text>
+    <path
+       style="fill:none;stroke:#008d34;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 38.79021,981.80275 c 0,46.50345 0.349651,45.80415 0.349651,45.80415 l 0,0.3496"
+       id="path3759-0-4"
+       inkscape:connector-curvature="0" />
+    <rect
+       style="fill:none;fill-opacity:1;stroke:#00a42f;stroke-width:0.98345959000000005;stroke-opacity:1"
+       id="rect3755"
+       width="110.58463"
+       height="46.853146"
+       x="6.9930072"
+       y="980.25031" />
+    <path
+       style="fill:none;stroke:#00b12f;stroke-width:0.98300683px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 6.993007,1003.6769 c 110.482823,-0.3497 110.482823,-0.3497 110.482823,-0.3497"
+       id="path3757"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#008d34;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 70.384615,980.94964 c 0,46.50346 0.349651,45.80416 0.349651,45.80416 l 0,0.3496"
+       id="path3759-0-4-7"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#008d34;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 102.38461,980.34824 c 0,46.50366 0.34966,45.80436 0.34966,45.80436 l 0,0.3496"
+       id="path3759-0-4-4"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 6.9930071,1025.7329 c 4.8951059,0 4.5454549,0 4.5454549,0"
+       id="path3980"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 15.461538,1015.481 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-4"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 15.48951,1020.6837 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-3"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 15.39161,1025.7328 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-0"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 39.251747,1025.7328 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-7"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 30.636363,1002.2084 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-8"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 54.370629,1002.3065 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-6"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 71,1002.1104 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-6-8"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 71,996.8098 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-6-84"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 71,991.15935 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-6-3"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 86.944047,1025.677 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-6-1"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 102.94406,1009.2153 c 4.8951,0 4.54545,0 4.54545,0"
+       id="path3980-6-4"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 102.94406,1015.2153 c 4.8951,0 4.54545,0 4.54545,0"
+       id="path3980-6-9"
+       inkscape:connector-curvature="0" />
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 103.04196,1020.67 c 4.8951,0 4.54545,0 4.54545,0"
+       id="path3980-6-2"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#1a1a1a;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
+       x="36.811184"
+       y="1007.2993"
+       id="text4114"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4116"
+         x="36.811184"
+         y="1007.2993"
+         style="font-size:72px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#1a1a1a;font-family:Sans;-inkscape-font-specification:Sans Bold">Q</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:72px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#71e572;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
+       x="32.798679"
+       y="1003.6852"
+       id="text4114-0"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4116-6"
+         x="32.798679"
+         y="1003.6852"
+         style="font-size:72px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#71e572;fill-opacity:1;font-family:Sans;-inkscape-font-specification:Sans Bold">Q</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:5.54682684px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00901d;fill-opacity:1;stroke:none;font-family:Sans"
+       x="1.5351157"
+       y="1106.4897"
+       id="text3870-36"
+       sodipodi:linespacing="125%"
+       transform="scale(1.112215,0.89910674)"><tspan
+         sodipodi:role="line"
+         id="tspan3872-1"
+         x="1.5351157"
+         y="1106.4897">1</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:5.54682684px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#00901d;fill-opacity:1;stroke:none;font-family:Sans"
+       x="1.5351157"
+       y="1131.8981"
+       id="text3870-0"
+       sodipodi:linespacing="125%"
+       transform="scale(1.112215,0.89910674)"><tspan
+         sodipodi:role="line"
+         id="tspan3872-6"
+         x="1.5351157"
+         y="1131.8981">0</tspan></text>
+    <path
+       style="fill:none;stroke:#4bdc7f;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 62.865204,1029.7242 c 4.895106,0 4.545455,0 4.545455,0"
+       id="path3980-4-3"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/src/Makefile.am b/src/Makefile.am
index e2385d0..c22c2d8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@ SUBDIRS = pixmaps
 bin_PROGRAMS = qmidiarp
 
 nodist_qmidiarp_SOURCES = \
-	arpdata_moc.cpp \
+	engine_moc.cpp \
 	arpscreen_moc.cpp \
 	lfoscreen_moc.cpp \
 	seqscreen_moc.cpp \
@@ -22,9 +22,9 @@ nodist_qmidiarp_SOURCES = \
 	jacksync_moc.cpp \
 	seqdriver_moc.cpp \
 	slider_moc.cpp
-	
+
 qmidiarp_SOURCES = \
-	arpdata.cpp arpdata.h \
+	engine.cpp engine.h \
 	arpscreen.cpp arpscreen.h \
 	lfoscreen.cpp lfoscreen.h \
 	seqscreen.cpp seqscreen.h \
@@ -39,22 +39,28 @@ qmidiarp_SOURCES = \
 	midilfo.cpp midilfo.h \
 	midiseq.cpp midiseq.h \
 	midicctable.cpp midicctable.h \
+	midievent.h \
 	passwidget.cpp passwidget.h \
 	jacksync.cpp jacksync.h \
 	seqdriver.cpp seqdriver.h \
 	slider.cpp slider.h
 
+qmidiarp_LDADD = $(QT_LIBS)
+
 translationsdir = $(pkgdatadir)/translations
 translations = \
+	translations/qmidiarp_cs.ts \
 	translations/qmidiarp_de.ts \
+	translations/qmidiarp_es.ts \
 	translations/qmidiarp_fr.ts
 
 translations_DATA = \
+	translations/qmidiarp_cs.qm \
 	translations/qmidiarp_de.qm \
+	translations/qmidiarp_es.qm \
 	translations/qmidiarp_fr.qm
 
 
-AM_LDFLAGS = @QT_LIBS@ @DEFS@
 AM_CXXFLAGS = @QT_CXXFLAGS@
 DEFS = -Wall -Wextra -D_REENTRANT -DTRANSLATIONSDIR=\"$(translationsdir)\" @DEFS@
 
diff --git a/src/Makefile.in b/src/Makefile.in
index 813b41f..3bac15c 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -38,7 +38,8 @@ subdir = src
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
 	$(srcdir)/config.h.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -47,25 +48,25 @@ CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(translationsdir)"
 PROGRAMS = $(bin_PROGRAMS)
-am_qmidiarp_OBJECTS = arpdata.$(OBJEXT) arpscreen.$(OBJEXT) \
+am_qmidiarp_OBJECTS = engine.$(OBJEXT) arpscreen.$(OBJEXT) \
 	lfoscreen.$(OBJEXT) seqscreen.$(OBJEXT) arpwidget.$(OBJEXT) \
 	lfowidget.$(OBJEXT) seqwidget.$(OBJEXT) groovewidget.$(OBJEXT) \
 	mainwindow.$(OBJEXT) logwidget.$(OBJEXT) main.$(OBJEXT) \
 	midiarp.$(OBJEXT) midilfo.$(OBJEXT) midiseq.$(OBJEXT) \
 	midicctable.$(OBJEXT) passwidget.$(OBJEXT) jacksync.$(OBJEXT) \
 	seqdriver.$(OBJEXT) slider.$(OBJEXT)
-nodist_qmidiarp_OBJECTS = arpdata_moc.$(OBJEXT) \
-	arpscreen_moc.$(OBJEXT) lfoscreen_moc.$(OBJEXT) \
-	seqscreen_moc.$(OBJEXT) arpwidget_moc.$(OBJEXT) \
-	lfowidget_moc.$(OBJEXT) seqwidget_moc.$(OBJEXT) \
-	groovewidget_moc.$(OBJEXT) mainwindow_moc.$(OBJEXT) \
-	logwidget_moc.$(OBJEXT) midiarp_moc.$(OBJEXT) \
-	midilfo_moc.$(OBJEXT) midiseq_moc.$(OBJEXT) \
-	midicctable_moc.$(OBJEXT) passwidget_moc.$(OBJEXT) \
-	jacksync_moc.$(OBJEXT) seqdriver_moc.$(OBJEXT) \
-	slider_moc.$(OBJEXT)
+nodist_qmidiarp_OBJECTS = engine_moc.$(OBJEXT) arpscreen_moc.$(OBJEXT) \
+	lfoscreen_moc.$(OBJEXT) seqscreen_moc.$(OBJEXT) \
+	arpwidget_moc.$(OBJEXT) lfowidget_moc.$(OBJEXT) \
+	seqwidget_moc.$(OBJEXT) groovewidget_moc.$(OBJEXT) \
+	mainwindow_moc.$(OBJEXT) logwidget_moc.$(OBJEXT) \
+	midiarp_moc.$(OBJEXT) midilfo_moc.$(OBJEXT) \
+	midiseq_moc.$(OBJEXT) midicctable_moc.$(OBJEXT) \
+	passwidget_moc.$(OBJEXT) jacksync_moc.$(OBJEXT) \
+	seqdriver_moc.$(OBJEXT) slider_moc.$(OBJEXT)
 qmidiarp_OBJECTS = $(am_qmidiarp_OBJECTS) $(nodist_qmidiarp_OBJECTS)
-qmidiarp_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+qmidiarp_DEPENDENCIES = $(am__DEPENDENCIES_1)
 DEFAULT_INCLUDES = -I. at am__isrc@
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
@@ -161,6 +162,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = -Wall -Wextra -D_REENTRANT -DTRANSLATIONSDIR=\"$(translationsdir)\" @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
@@ -249,7 +275,7 @@ top_srcdir = @top_srcdir@
 # Makefile.am for qmidiarp
 SUBDIRS = pixmaps
 nodist_qmidiarp_SOURCES = \
-	arpdata_moc.cpp \
+	engine_moc.cpp \
 	arpscreen_moc.cpp \
 	lfoscreen_moc.cpp \
 	seqscreen_moc.cpp \
@@ -269,7 +295,7 @@ nodist_qmidiarp_SOURCES = \
 	slider_moc.cpp
 
 qmidiarp_SOURCES = \
-	arpdata.cpp arpdata.h \
+	engine.cpp engine.h \
 	arpscreen.cpp arpscreen.h \
 	lfoscreen.cpp lfoscreen.h \
 	seqscreen.cpp seqscreen.h \
@@ -284,21 +310,26 @@ qmidiarp_SOURCES = \
 	midilfo.cpp midilfo.h \
 	midiseq.cpp midiseq.h \
 	midicctable.cpp midicctable.h \
+	midievent.h \
 	passwidget.cpp passwidget.h \
 	jacksync.cpp jacksync.h \
 	seqdriver.cpp seqdriver.h \
 	slider.cpp slider.h
 
+qmidiarp_LDADD = $(QT_LIBS)
 translationsdir = $(pkgdatadir)/translations
 translations = \
+	translations/qmidiarp_cs.ts \
 	translations/qmidiarp_de.ts \
+	translations/qmidiarp_es.ts \
 	translations/qmidiarp_fr.ts
 
 translations_DATA = \
+	translations/qmidiarp_cs.qm \
 	translations/qmidiarp_de.qm \
+	translations/qmidiarp_es.qm \
 	translations/qmidiarp_fr.qm
 
-AM_LDFLAGS = @QT_LIBS@ @DEFS@
 AM_CXXFLAGS = @QT_CXXFLAGS@
 
 # misc files which are distributed but not installed
@@ -408,12 +439,12 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arpdata.Po at am__quote@
- at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arpdata_moc.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arpscreen.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arpscreen_moc.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arpwidget.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/arpwidget_moc.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/engine.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/engine_moc.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/groovewidget.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/groovewidget_moc.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/jacksync.Po at am__quote@
diff --git a/src/arpdata.h b/src/arpdata.h
deleted file mode 100644
index afe8909..0000000
--- a/src/arpdata.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef ARPDATA_H
-#define ARPDATA_H
-
-#include <QWidget>
-#include <QDockWidget>
-#include <QList>
-#include "seqdriver.h"
-#include "midiarp.h"
-#include "arpwidget.h"
-#include "midilfo.h"
-#include "lfowidget.h"
-#include "midiseq.h"
-#include "seqwidget.h"
-
-class ArpData : public QWidget  {
-    
-  Q_OBJECT
-
-  private:
-    QList<MidiArp *> midiArpList;
-    QList<ArpWidget *> arpWidgetList;
-    QList<QDockWidget *> moduleWindowList;
-    QList<MidiLfo *> midiLfoList;
-    QList<LfoWidget *> lfoWidgetList;
-    QList<MidiSeq *> midiSeqList;
-    QList<SeqWidget *> seqWidgetList;
-    int portCount;
-    bool modified;
-    int mute_ccnumber, midiLearnID, midiLearnWindowID, midiLearnModuleID;
-    bool midi_mutable, midiLearnFlag;
-    
-  public:
-    SeqDriver *seqDriver;
-
-    ArpData(QWidget* parent=0);
-    ~ArpData();
-    void registerPorts(int num);
-    int getPortCount();
-    bool isModified();
-
-
-    void addModuleWindow(QDockWidget *moduleWindow);
-    void removeModuleWindow(QDockWidget *moduleWindow);
-    QDockWidget *moduleWindow(int index);
-    int moduleWindowCount();
-    void updateIDs(int curID);
-    
-    void addMidiArp(MidiArp *midiArp);
-    void addArpWidget(ArpWidget *arpWidget);
-    void removeMidiArp(MidiArp *midiArp);
-    void removeArpWidget(ArpWidget *arpWidget);
-    int midiArpCount();
-    int arpWidgetCount();
-    MidiArp *midiArp(int index);
-    ArpWidget *arpWidget(int index);
-    
-    void addMidiLfo(MidiLfo *midiLfo);
-    void addLfoWidget(LfoWidget *lfoWidget);
-    void removeMidiLfo(MidiLfo *midiLfo);
-    void removeLfoWidget(LfoWidget *lfoWidget);
-    int midiLfoCount();
-    int lfoWidgetCount();   
-    MidiLfo *midiLfo(int index);
-    LfoWidget *lfoWidget(int index);
-    
-    void addMidiSeq(MidiSeq *midiSeq);
-    void addSeqWidget(SeqWidget *seqWidget);
-    void removeMidiSeq(MidiSeq *midiSeq);
-    void removeSeqWidget(SeqWidget *seqWidget);
-    int midiSeqCount();
-    int seqWidgetCount();   
-    MidiSeq *midiSeq(int index);
-    SeqWidget *seqWidget(int index);
-    int getAlsaClientId();
-    
-  public slots:
-    void runQueue(bool);
-    void setModified(bool);
-    void updatePatternPresets(const QString& n, const QString& p, int index);
-    void handleController(int ccnumber, int channel, int value);
-    void setMidiLearn(int moduleWindowID, int moduleID, int controlID);
-    void setCompactStyle(bool on);
-};
-                              
-#endif
diff --git a/src/arpscreen.cpp b/src/arpscreen.cpp
index b1a01eb..a2207a0 100644
--- a/src/arpscreen.cpp
+++ b/src/arpscreen.cpp
@@ -1,37 +1,34 @@
-/*
- *      arpscreen.cpp
- *      
- *      This file is part of QMidiArp.
- * 
+/*!
+ * @file arpscreen.cpp
+ * @brief Implementation of the ArpScreen class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
-#include <QPolygon>
 #include <QPainter>
 #include <QPaintDevice>
 #include <QPen>
 #include <QPixmap>
-#include <QBrush>
-#include <QSizePolicy>
-#include <QSize>
-
-#include <alsa/asoundlib.h>
 
 #include "arpscreen.h"
-#include "arpdata.h"
 
 
 ArpScreen::ArpScreen(QWidget* parent) : QWidget (parent)
@@ -52,7 +49,6 @@ ArpScreen::~ArpScreen()
 void ArpScreen::paintEvent(QPaintEvent*)
 {
     QPainter p(this);
-    QPolygon points(7);
     QPen pen;
     pen.setWidth(1);
     p.setFont(QFont("Helvetica", 8));
@@ -61,9 +57,9 @@ void ArpScreen::paintEvent(QPaintEvent*)
     int l1, l2;
     int beat = 4;
     double nsteps = 0.0;
-    double tempo = 1.0;
+    double stepWidth = 1.0;
     double curstep = 0.0;
-    int nlines = 0; 
+    int nlines = 0;
     int notelen;
     int ypos, xpos;
     int octYoffset;
@@ -82,7 +78,7 @@ void ArpScreen::paintEvent(QPaintEvent*)
     double minTempo = 1.0;
     int noctaves = 1;
     double vel =1.0;
-    int grooveTmp = 0;
+    //~ int grooveTmp = 0;
     int grooveIndex = 0;
     int chordIndex = 0;
     l2 = 0;
@@ -91,13 +87,13 @@ void ArpScreen::paintEvent(QPaintEvent*)
     patternLen = a_pattern.length();
     patternMaxIndex = 0;
 
-    for (l1 = 0; l1 < patternLen; l1++) 
+    for (l1 = 0; l1 < patternLen; l1++)
     {
         c = a_pattern.at(l1);
 
         if (c.isDigit()) {
             if (!chordIndex) {
-                nsteps += tempo;
+                nsteps += stepWidth;
                 if (chordMode) chordIndex++;
             }
             if (c.digitValue() > patternMaxIndex)
@@ -115,22 +111,22 @@ void ArpScreen::paintEvent(QPaintEvent*)
                 break;
 
             case '>':
-                tempo /= 2.0;
-                if (tempo < minTempo)
+                stepWidth /= 2.0;
+                if (stepWidth < minTempo)
                     minTempo /= 2.0;
                 break;
 
             case '<':
-                tempo *= 2.0;
+                stepWidth *= 2.0;
                 break;
 
             case '.':
-                tempo = 1.0;
+                stepWidth = 1.0;
                 break;
-                
+
             case 'p':
                 if (!chordMode)
-                    nsteps += tempo;
+                    nsteps += stepWidth;
                 break;
 
             case '+':
@@ -155,13 +151,13 @@ void ArpScreen::paintEvent(QPaintEvent*)
 
             case '\\':
                 vel -= 0.2;
-                break; 
+                break;
 
             case 'd':
                 notelen *= 2;
                 break;
 
-            case 'h': 
+            case 'h':
                 notelen /= 2;
                 break;
 
@@ -171,9 +167,9 @@ void ArpScreen::paintEvent(QPaintEvent*)
 
     }
 
-    
+
     //Green Filled Frame
-    if (isMuted) 
+    if (isMuted)
         p.fillRect(0, 0, w, h, QColor(70, 70, 70));
     else
         p.fillRect(0, 0, w, h, QColor(10, 50, 10));
@@ -183,32 +179,32 @@ void ArpScreen::paintEvent(QPaintEvent*)
     p.setPen(QColor(20, 160, 20));
     p.drawRect(0, 0, w - 1, h - 1);
 
-    //Grid 
-    len = nsteps;  
-    xscale = (w - 2 * ARPSCREEN_HMARGIN) / len;
-    yscale = h - 2 * ARPSCREEN_VMARGIN;
+    //Grid
+    len = nsteps;
+    xscale = (w - 2 * ARPSCR_HMARG) / len;
+    yscale = h - 2 * ARPSCR_VMARG;
 
     //Beat separators
     for (l1 = 0; l1 < nsteps + 1; l1++) {
 
         if (l1 < 10) {
-            ofs = w / len * .5 - 4 + ARPSCREEN_HMARGIN;
+            ofs = w / len * .5 - 4 + ARPSCR_HMARG;
         } else {
-            ofs = w / len * .5 - 6 + ARPSCREEN_HMARGIN;
+            ofs = w / len * .5 - 6 + ARPSCR_HMARG;
         }
         if ((bool)(l1%beat)) {
             p.setPen(QColor(60, 180, 60));
         } else {
-            p.setPen(QColor(60, 180, 150));   
+            p.setPen(QColor(60, 180, 150));
         }
         x = l1 * xscale;
-        p.drawLine(ARPSCREEN_HMARGIN + x, ARPSCREEN_VMARGIN,
-                ARPSCREEN_HMARGIN + x, h-ARPSCREEN_VMARGIN);
+        p.drawLine(ARPSCR_HMARG + x, ARPSCR_VMARG,
+                ARPSCR_HMARG + x, h-ARPSCR_VMARG);
 
         if (l1 < nsteps) {
 
             //Beat numbers
-            p.drawText(ofs + x, ARPSCREEN_VMARGIN, QString::number(l1+1));
+            p.drawText(ofs + x, ARPSCR_VMARG, QString::number(l1+1));
 
             // Beat divisor separators
             p.setPen(QColor(40, 100, 40));
@@ -216,10 +212,10 @@ void ArpScreen::paintEvent(QPaintEvent*)
             for (l2 = 1; l2 < 1.0/minTempo; l2++) {
                 x1 = x + l2 * xscale * minTempo;
                 if (x1 < xscale*len)
-                    p.drawLine(ARPSCREEN_HMARGIN + x1,
-                            ARPSCREEN_VMARGIN, ARPSCREEN_HMARGIN + x1,
-                            h - ARPSCREEN_VMARGIN);
-            } 
+                    p.drawLine(ARPSCR_HMARG + x1,
+                            ARPSCR_VMARG, ARPSCR_HMARG + x1,
+                            h - ARPSCR_VMARG);
+            }
         }
     }
 
@@ -227,40 +223,40 @@ void ArpScreen::paintEvent(QPaintEvent*)
     p.setPen(QColor(40, 120, 40));
     noctaves = maxOctave - minOctave + 1;
     for (l1 = 0; l1 < noctaves + 1; l1++) {
-        ypos = yscale * l1 / noctaves + ARPSCREEN_VMARGIN;
-        p.drawLine(ARPSCREEN_HMARGIN, ypos, w - ARPSCREEN_HMARGIN, ypos);        
-        p.drawText(ARPSCREEN_HMARGIN / 2 - 3, 
-                yscale * (l1 + 0.5) / noctaves + ARPSCREEN_VMARGIN + 4, 
+        ypos = yscale * l1 / noctaves + ARPSCR_VMARG;
+        p.drawLine(ARPSCR_HMARG, ypos, w - ARPSCR_HMARG, ypos);
+        p.drawText(ARPSCR_HMARG / 2 - 3,
+                yscale * (l1 + 0.5) / noctaves + ARPSCR_VMARG + 4,
                 QString::number(noctaves - l1 + minOctave - 1));
-    }    
+    }
 
     //Draw arpTicks
     curstep= 0.0;
     notelen = xscale/8;
-    tempo = 1.0;
+    stepWidth = 1.0;
     vel = 0.8;
     octave = 0;
     chordMode = false;
     chordIndex = 0;
 
 
-    for (l1 = 0; l1 < patternLen; l1++) 
+    for (l1 = 0; l1 < patternLen; l1++)
     {
         c = a_pattern.at(l1);
-        grooveTmp = (grooveIndex % 2) ? -grooveTick : grooveTick ;
-        if (c.isDigit()) 
+        //~ grooveTmp = (grooveIndex % 2) ? -grooveTick : grooveTick ;
+        if (c.isDigit())
         {
             nlines = c.digitValue() + 1;
-            if (!chordIndex) 
+            if (!chordIndex)
             {
                 if (chordMode) chordIndex++;
-                curstep += tempo; // * (1.0 + 0.01 * (double)grooveTmp); 
-                grooveIndex++; 
+                curstep += stepWidth; // * (1.0 + 0.01 * (double)grooveTmp);
+                grooveIndex++;
             }
         }
-        else 
+        else
         {
-            switch (c.toAscii()) 
+            switch (c.toAscii())
             {
                 case '(':
                     chordMode = true;
@@ -273,21 +269,21 @@ void ArpScreen::paintEvent(QPaintEvent*)
                     break;
 
                 case '>':
-                    tempo /= 2.0;
+                    stepWidth /= 2.0;
                     break;
 
                 case '<':
-                    tempo *= 2.0;
+                    stepWidth *= 2.0;
                     break;
 
                 case '.':
-                    tempo = 1.0;
+                    stepWidth = 1.0;
                     break;
 
                 case 'p':
                     if (!chordMode)
-                        curstep += tempo; // * (1.0 + 0.01 * (double)grooveTmp);
-                        grooveIndex++; 
+                        curstep += stepWidth; // * (1.0 + 0.01 * (double)grooveTmp);
+                        grooveIndex++;
                    break;
 
                 case '+':
@@ -320,48 +316,40 @@ void ArpScreen::paintEvent(QPaintEvent*)
 
                 default:
                     ;
-            }   
+            }
         }
 
-        if (c.isDigit()) 
+        if (c.isDigit())
         {
             octYoffset = (octave - minOctave) * (patternMaxIndex + 1);
-            x = (curstep - tempo) * xscale;
-//          notestreak_thick = h / (patternMaxIndex + 1) / noctaves / 2;
-            if (nlines > 0) 
+            x = (curstep - stepWidth) * xscale;
+            if (nlines > 0)
             {
+                pen.setWidth(notestreak_thick);
+                pen.setColor(QColor(80 + 60 * (vel - 0.8),
+                            160 + 40 * (vel - 0.8),
+                            80 + 60 * (vel - 0.8)));
+                p.setPen(pen);
                 ypos = yscale - yscale * (nlines - 1 + octYoffset)
                             / (patternMaxIndex + 1) / noctaves
-                            + ARPSCREEN_VMARGIN - 3 + notestreak_thick;
-                xpos = ARPSCREEN_HMARGIN + x + notestreak_thick / 2;
-                if (grooveIndex == currentIndex) 
-                {
-                    pen.setColor(QColor(140, 240, 140));
-                    p.setPen(pen);
-                    p.drawLine(ARPSCREEN_HMARGIN + x, ARPSCREEN_VMARGIN,
-                            ARPSCREEN_HMARGIN + x, h);
-                    pen.setWidth(notestreak_thick);
-                    pen.setColor(QColor(80 + 60 * (vel - 0.8),
-                               250, 120 + 60 * (vel - 0.8)));
+                            + ARPSCR_VMARG - 3 + notestreak_thick;
+                xpos = ARPSCR_HMARG + x + pen.width() / 2;
+                p.drawLine(xpos, ypos,
+                        xpos + notelen - pen.width(), ypos);
+                // Cursor
+                if (grooveIndex == currentIndex) {
+                    pen.setWidth(notestreak_thick * 2);
                     p.setPen(pen);
+                    ypos = h - 2;
+                    xpos = ARPSCR_HMARG + x + pen.width() / 2;
                     p.drawLine(xpos, ypos,
-                            xpos + notelen - notestreak_thick / 2, ypos);
-                } else 
-                {
-                    pen.setWidth(notestreak_thick);
-                    pen.setColor(QColor(80 + 60 * (vel - 0.8),
-                                160 + 40 * (vel - 0.8),
-                                80 + 60 * (vel - 0.8)));
-                    p.setPen(pen);
-                    p.drawLine(xpos, ypos,
-                            xpos + notelen - notestreak_thick / 2, ypos);
+                        xpos + notelen - pen.width(), ypos);
                 }
                 pen.setWidth(1);
 
             }
         }
     }
-
 }
 
 void ArpScreen::updateScreen(const QString& pattern)
@@ -401,7 +389,7 @@ void ArpScreen::setMuted(bool on)
 
 QSize ArpScreen::sizeHint() const
 {
-    return QSize(ARPSCREEN_MINIMUM_WIDTH, ARPSCREEN_MINIMUM_HEIGHT); 
+    return QSize(ARPSCR_MIN_W, ARPSCR_MIN_H);
 }
 
 QSizePolicy ArpScreen::sizePolicy() const
diff --git a/src/arpscreen.h b/src/arpscreen.h
index 9dd5aff..20dd494 100644
--- a/src/arpscreen.h
+++ b/src/arpscreen.h
@@ -1,3 +1,27 @@
+/*!
+ * @file arpscreen.h
+ * @brief Header for the ArpScreen class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef ARPSCREEN_H
 #define ARPSCREEN_H
 
@@ -7,16 +31,27 @@
 #include <QTimer>
 #include <QSizePolicy>
 #include <QSize>
-#include <alsa/asoundlib.h>
 
-#define ARPSCREEN_MINIMUM_WIDTH   250
-#define ARPSCREEN_MINIMUM_HEIGHT  120
-#define ARPSCREEN_VMARGIN          10
-#define ARPSCREEN_HMARGIN          16
+#include "midilfo.h"
 
+#define ARPSCR_MIN_W    250
+#define ARPSCR_MIN_H    120
+#define ARPSCR_VMARG    10
+#define ARPSCR_HMARG    16
 
 
 
+
+/*! @brief Drawing widget for visualization of arp patterns using QPainter
+ *
+ * ArpScreen is created and embedded by ArpWidget. The painter callback
+ * analyses the pattern string and produces a streak map of its content
+ * similar to a piano roll display. The display is updated
+ * by calling ArpScreen::updateScreen() with the pattern text string as
+ * and argument. A cursor is placed at the corresponding pattern index
+ * by calling ArpScreen::updateScreen() with the integer current pattern
+ * index as an overloaded member.
+ */
 class ArpScreen : public QWidget
 {
   Q_OBJECT
@@ -27,17 +62,17 @@ class ArpScreen : public QWidget
     QString a_pattern;
     int pattern_updated, currentIndex;
     bool isMuted;
-    
+
   protected:
     virtual void paintEvent(QPaintEvent *);
-    
+
   public:
     ArpScreen(QWidget* parent=0);
     ~ArpScreen();
     virtual QSize sizeHint() const;
     virtual QSizePolicy sizePolicy() const;
-   
-  public slots: 
+
+  public slots:
     void updateScreen(const QString&);
     void updateScreen(int p_index);
     void setGrooveTick(int tick);
@@ -45,5 +80,5 @@ class ArpScreen : public QWidget
     void setGrooveLength(int length);
     void setMuted(bool on);
 };
-  
+
 #endif
diff --git a/src/arpwidget.cpp b/src/arpwidget.cpp
index 0f493c5..a600987 100644
--- a/src/arpwidget.cpp
+++ b/src/arpwidget.cpp
@@ -1,3 +1,27 @@
+/*!
+ * @file arpwidget.cpp
+ * @brief Implements the ArpWidget GUI class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
 #include <QBoxLayout>
 #include <QStringList>
 #include <QFile>
@@ -23,8 +47,25 @@
 
 ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QWidget *parent)
 : QWidget(parent), midiWorker(p_midiWorker), modified(false)
-{    
-    QGridLayout *widgetLayout = new QGridLayout;
+{
+    int l1;
+
+    // QSignalMappers allow identifying signal senders for MIDI learn/forget
+    learnSignalMapper = new QSignalMapper(this);
+    connect(learnSignalMapper, SIGNAL(mapped(int)),
+             this, SLOT(midiLearn(int)));
+
+    forgetSignalMapper = new QSignalMapper(this);
+    connect(forgetSignalMapper, SIGNAL(mapped(int)),
+             this, SLOT(midiForget(int)));
+
+    // we need the cancel MIDI Learn action only once for all
+    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
+    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
+    cancelMidiLearnAction->setEnabled(false);
+
+    midiCCNames << "MuteToggle" << "PresetSwitch" << "unknown";
+
 
     // Management Buttons on the right top
     QHBoxLayout *manageBoxLayout = new QHBoxLayout;
@@ -34,26 +75,25 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     QToolButton *renameButton = new QToolButton(this);
     renameButton->setDefaultAction(renameAction);
     connect(renameAction, SIGNAL(triggered()), this, SLOT(moduleRename()));
-    
+
     deleteAction = new QAction(QIcon(arpremove_xpm), tr("&Delete..."), this);
     deleteAction->setToolTip(tr("Delete this Arp"));
     QToolButton *deleteButton = new QToolButton(this);
     deleteButton->setDefaultAction(deleteAction);
     connect(deleteAction, SIGNAL(triggered()), this, SLOT(moduleDelete()));
-    
+
     manageBoxLayout->addStretch();
     manageBoxLayout->addWidget(renameButton);
     manageBoxLayout->addWidget(deleteButton);
-    
+
     // Input group box on left side
     QGroupBox *inBox = new QGroupBox(tr("Input"), this);
-    
+
     QLabel *chInLabel = new QLabel(tr("&Channel"), inBox);
-    chIn = new QSpinBox(inBox);
-    chIn->setRange(1, 16);
-    chIn->setKeyboardTracking(false);
+    chIn = new QComboBox(inBox);
+    for (l1 = 0; l1 < 16; l1++) chIn->addItem(QString::number(l1 + 1));
     chInLabel->setBuddy(chIn);
-    connect(chIn, SIGNAL(valueChanged(int)), this, SLOT(updateChIn(int)));
+    connect(chIn, SIGNAL(activated(int)), this, SLOT(updateChIn(int)));
 
     inputFilterBox = new QGroupBox(tr("Note Filter"));
     indexInLabel = new QLabel(tr("&Note"), inputFilterBox);
@@ -91,16 +131,16 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     inputFilterBoxLayout->addWidget(indexIn[1], 0, 2);
     inputFilterBoxLayout->addWidget(rangeInLabel, 1, 0);
     inputFilterBoxLayout->addWidget(rangeIn[0], 1, 1);
-    inputFilterBoxLayout->addWidget(rangeIn[1], 1, 2);    
+    inputFilterBoxLayout->addWidget(rangeIn[1], 1, 2);
     inputFilterBoxLayout->setMargin(2);
     inputFilterBoxLayout->setSpacing(2);
     inputFilterBox->setCheckable(true);
-    connect(inputFilterBox, SIGNAL(toggled(bool)), this, 
+    connect(inputFilterBox, SIGNAL(toggled(bool)), this,
             SLOT(setInputFilterVisible(bool)));
     inputFilterBox->setChecked(false);
     inputFilterBox->setFlat(true);
     inputFilterBox->setLayout(inputFilterBoxLayout);
-    
+
     QGridLayout *inBoxLayout = new QGridLayout;
     inBoxLayout->addWidget(chInLabel, 0, 0);
     inBoxLayout->addWidget(chIn, 0, 1);
@@ -109,7 +149,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
         inBoxLayout->setMargin(2);
         inBoxLayout->setSpacing(1);
     }
-    inBox->setLayout(inBoxLayout); 
+    inBox->setLayout(inBoxLayout);
 
 
     // Output group box on right side
@@ -117,35 +157,33 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
 
     QLabel *muteLabel = new QLabel(tr("&Mute"),portBox);
     muteOut = new QCheckBox(this);
-
     muteOut->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
+    connect(muteOut, SIGNAL(toggled(bool)), this, SLOT(setMuted(bool)));
+    muteLabel->setBuddy(muteOut);
+
     QAction *muteLearnAction = new QAction(tr("MIDI &Learn"), this);
     muteOut->addAction(muteLearnAction);
-    connect(muteLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnMute()));
-    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
-    cancelMidiLearnAction->setEnabled(false);
-    muteOut->addAction(cancelMidiLearnAction);
-    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
+    connect(muteLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(muteLearnAction, 0);
+
     QAction *muteForgetAction = new QAction(tr("MIDI &Forget"), this);
     muteOut->addAction(muteForgetAction);
-    connect(muteForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetMute()));
+    connect(muteForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(muteForgetAction, 0);
 
-    connect(muteOut, SIGNAL(toggled(bool)), this, SLOT(setMuted(bool)));
-    muteLabel->setBuddy(muteOut);
+    muteOut->addAction(cancelMidiLearnAction);
 
     QLabel *portLabel = new QLabel(tr("&Port"), portBox);
-    portOut = new QSpinBox(portBox);
+    portOut = new QComboBox(portBox);
     portLabel->setBuddy(portOut);
-    portOut->setRange(1, portCount);
-    portOut->setKeyboardTracking(false);
-    connect(portOut, SIGNAL(valueChanged(int)), this, SLOT(updatePortOut(int)));
+    for (l1 = 0; l1 < portCount; l1++) portOut->addItem(QString::number(l1 + 1));
+    connect(portOut, SIGNAL(activated(int)), this, SLOT(updatePortOut(int)));
 
     QLabel *channelLabel = new QLabel(tr("C&hannel"), portBox);
-    channelOut = new QSpinBox(portBox);
+    channelOut = new QComboBox(portBox);
     channelLabel->setBuddy(channelOut);
-    channelOut->setRange(1, 16);
-    channelOut->setKeyboardTracking(false);
-    connect(channelOut, SIGNAL(valueChanged(int)), this,
+    for (l1 = 0; l1 < 16; l1++) channelOut->addItem(QString::number(l1 + 1));
+    connect(channelOut, SIGNAL(activated(int)), this,
             SLOT(updateChannelOut(int)));
 
     QGridLayout *portBoxLayout = new QGridLayout;
@@ -173,7 +211,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     QGroupBox *patternBox = new QGroupBox(tr("Pattern"), this);
     QVBoxLayout *patternBoxLayout = new QVBoxLayout;
 
-    textEditButton = new QToolButton(this); 
+    textEditButton = new QToolButton(this);
     textEditAction = new QAction(QIcon(editmodeon_xpm),
             tr("&Edit Pattern"), this);
     connect(textEditAction, SIGNAL(toggled(bool)), this,
@@ -181,7 +219,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     textEditAction->setCheckable(true);
     textEditButton->setDefaultAction(textEditAction);
 
-    textRemoveButton = new QToolButton(this);   
+    textRemoveButton = new QToolButton(this);
     textRemoveAction = new QAction(QIcon(patternremove_xpm),
             tr("&Remove Pattern"), this);
     connect(textRemoveAction, SIGNAL(triggered()), this,
@@ -205,21 +243,22 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     patternPresetBox->setMinimumContentsLength(20);
     connect(patternPresetBox, SIGNAL(activated(int)), this,
             SLOT(selectPatternPreset(int)));
-            
     patternPresetBox->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
+
     QAction *presetSwitchLearnAction = new QAction(tr("MIDI &Learn"), this);
     patternPresetBox->addAction(presetSwitchLearnAction);
-    connect(presetSwitchLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnPresetSwitch()));
-    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
-    cancelMidiLearnAction->setEnabled(false);
-    patternPresetBox->addAction(cancelMidiLearnAction);
-    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
+    connect(presetSwitchLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(presetSwitchLearnAction, 1);
+
     QAction *presetSwitchForgetAction = new QAction(tr("MIDI &Forget"), this);
     patternPresetBox->addAction(presetSwitchForgetAction);
-    connect(presetSwitchForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetPresetSwitch()));
+    connect(presetSwitchForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(presetSwitchForgetAction, 1);
+
+    patternPresetBox->addAction(cancelMidiLearnAction);
 
     repeatPatternThroughChord = new QComboBox(patternBox);
-    QStringList repeatPatternNames; 
+    QStringList repeatPatternNames;
     repeatPatternNames << tr("Static") << tr("Up") << tr("Down");
     repeatPatternThroughChord->insertItems(0, repeatPatternNames);
     repeatPatternThroughChord->setToolTip(tr("Repeat mode"));
@@ -228,7 +267,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     repeatPatternThroughChord->setCurrentIndex(1);
 
     triggerMode = new QComboBox(patternBox);
-    QStringList triggerModeNames; 
+    QStringList triggerModeNames;
     triggerModeNames << tr("No trigger") << tr("Kbd restart") << tr("Kbd trigger");
     triggerMode->insertItems(0, triggerModeNames);
     triggerMode->setToolTip(tr("Trigger Mode"));
@@ -236,10 +275,10 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
             SLOT(updateTriggerMode(int)));
     triggerMode->setCurrentIndex(0);
 
-    latchModeButton = new QToolButton(this); 
+    latchModeButton = new QToolButton(this);
     latchModeAction = new QAction(QIcon(latchmodeon_xpm),
             tr("&Latch Mode"), this);
-    connect(latchModeAction, SIGNAL(toggled(bool)), midiWorker,
+    connect(latchModeAction, SIGNAL(toggled(bool)), this,
             SLOT(setLatchMode(bool)));
     latchModeAction->setCheckable(true);
     latchModeButton->setDefaultAction(latchModeAction);
@@ -251,22 +290,22 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     }
     patternPresetLayout->addWidget(patternPresetBox);
     patternPresetLayout->addWidget(textEditButton);
-    patternPresetLayout->addWidget(textStoreButton);    
+    patternPresetLayout->addWidget(textStoreButton);
     patternPresetLayout->addWidget(textRemoveButton);
     patternPresetLayout->addStretch(2);
-    
+
     QHBoxLayout *modeLayout = new QHBoxLayout;
     if (compactStyle) {
         modeLayout->setMargin(2);
         modeLayout->setSpacing(1);
     }
 
-    modeLayout->addWidget(repeatPatternThroughChord);  
-    modeLayout->addWidget(triggerMode);  
+    modeLayout->addWidget(repeatPatternThroughChord);
+    modeLayout->addWidget(triggerMode);
     modeLayout->addWidget(latchModeButton);
     modeLayout->addStretch(2);
 
-    patternText = new QLineEdit(patternBox); 
+    patternText = new QLineEdit(patternBox);
     connect(patternText, SIGNAL(textChanged(const QString&)), this,
             SLOT(updateText(const QString&)));
     patternText->setHidden(true);
@@ -282,7 +321,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
 
     QWidget *screenBox = new QWidget(patternBox);
     QHBoxLayout *screenBoxLayout = new QHBoxLayout;
-    screen = new ArpScreen(patternBox); 
+    screen = new ArpScreen(patternBox);
     screenBox->setMinimumHeight(80);
     screenBoxLayout->addWidget(screen);
     screenBoxLayout->setMargin(2);
@@ -297,7 +336,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
         patternBoxLayout->setMargin(2);
         patternBoxLayout->setSpacing(1);
     }
-    patternBox->setLayout(patternBoxLayout); 
+    patternBox->setLayout(patternBoxLayout);
 
     // group box for random settings
     randomBox = new QGroupBox(tr("Random"), this);
@@ -316,8 +355,8 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
     randomLength = new Slider(0, 100, 1, 5, 0, Qt::Horizontal,
             tr("&Length"), randomBox);
     connect(randomLength, SIGNAL(valueChanged(int)), this,
-            SLOT(updateRandomLengthAmp(int))); 
-             
+            SLOT(updateRandomLengthAmp(int)));
+
     randomBoxLayout->addWidget(randomTick);
     randomBoxLayout->addWidget(randomVelocity);
     randomBoxLayout->addWidget(randomLength);
@@ -327,12 +366,12 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
         randomBoxLayout->setMargin(2);
     }
     randomBox->setCheckable(true);
-    connect(randomBox, SIGNAL(toggled(bool)), this, 
+    connect(randomBox, SIGNAL(toggled(bool)), this,
             SLOT(setRandomVisible(bool)));
     randomBox->setChecked(false);
     randomBox->setFlat(true);
     randomBox->setLayout(randomBoxLayout);
-    
+
     envelopeBox = new QGroupBox(tr("Envelope"), this);
     QVBoxLayout *envelopeBoxLayout = new QVBoxLayout;
     attackTime = new Slider(0, 20, 1, 1, 0, Qt::Horizontal,
@@ -343,7 +382,7 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
             tr("&Release (s)"), envelopeBox);
     connect(releaseTime, SIGNAL(valueChanged(int)), this,
             SLOT(updateReleaseTime(int)));
-              
+
     envelopeBoxLayout->addWidget(attackTime);
     envelopeBoxLayout->addWidget(releaseTime);
     envelopeBoxLayout->addStretch();
@@ -352,12 +391,13 @@ ArpWidget::ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QW
         envelopeBoxLayout->setMargin(2);
     }
     envelopeBox->setCheckable(true);
-    connect(envelopeBox, SIGNAL(toggled(bool)), this, 
+    connect(envelopeBox, SIGNAL(toggled(bool)), this,
             SLOT(setEnvelopeVisible(bool)));
     envelopeBox->setChecked(false);
     envelopeBox->setFlat(true);
     envelopeBox->setLayout(envelopeBoxLayout);
-    
+
+    QGridLayout *widgetLayout = new QGridLayout;
     widgetLayout->addWidget(patternBox, 0, 0);
     widgetLayout->addWidget(randomBox, 1, 0);
     widgetLayout->addWidget(envelopeBox, 2, 0);
@@ -379,26 +419,26 @@ MidiArp *ArpWidget::getMidiWorker()
 
 void ArpWidget::updateChIn(int value)
 {
-    midiWorker->chIn = value - 1;
+    midiWorker->chIn = value;
 }
 
 void ArpWidget::updateIndexIn(int value)
 {
     if (indexIn[0] == sender()) {
-        midiWorker->indexIn[0] = value; 
+        midiWorker->indexIn[0] = value;
     } else {
         midiWorker->indexIn[1] = value;
-    }  
+    }
     checkIfInputFilterSet();
 }
 
 void ArpWidget::updateRangeIn(int value)
-{ 
+{
     if (rangeIn[0] == sender()) {
-        midiWorker->rangeIn[0] = value; 
+        midiWorker->rangeIn[0] = value;
     } else {
         midiWorker->rangeIn[1] = value;
-    }  
+    }
     checkIfInputFilterSet();
 }
 
@@ -408,43 +448,27 @@ void ArpWidget::checkIfInputFilterSet()
             || ((rangeIn[1]->value() - rangeIn[0]->value()) < 127)) {
         inputFilterBox->setFlat(false);
         inputFilterBox->setTitle(tr("Note Filter - ACTIVE"));
-    } 
+    }
     else {
         inputFilterBox->setFlat(true);
         inputFilterBox->setTitle(tr("Note Filter"));
     }
 }
 
-void ArpWidget::setMuted(bool on)
-{
-    midiWorker->setMuted(on);
-    screen->setMuted(on);
-}
-
-void ArpWidget::updatePortOut(int value)
-{
-    midiWorker->portOut = value - 1;
-}
-
-void ArpWidget::updateChannelOut(int value)
-{
-    midiWorker->channelOut = value - 1;
-}
-
 void ArpWidget::writeData(QXmlStreamWriter& xml)
 {
     xml.writeStartElement(name.left(3));
     xml.writeAttribute("name", name.mid(name.indexOf(':') + 1));
         xml.writeStartElement("pattern");
-            xml.writeTextElement("pattern", midiWorker->pattern);        
+            xml.writeTextElement("pattern", midiWorker->pattern);
             xml.writeTextElement("repeatMode", QString::number(
                 midiWorker->repeatPatternThroughChord));
             xml.writeTextElement("triggerMode", QString::number(
                 triggerMode->currentIndex()));
             xml.writeTextElement("latchMode", QString::number(
-                latchModeAction->isChecked()));           
+                latchModeAction->isChecked()));
         xml.writeEndElement();
-            
+
         xml.writeStartElement("input");
             xml.writeTextElement("channel", QString::number(
                 midiWorker->chIn));
@@ -457,14 +481,14 @@ void ArpWidget::writeData(QXmlStreamWriter& xml)
             xml.writeTextElement("rangeMax", QString::number(
                 midiWorker->rangeIn[1]));
         xml.writeEndElement();
-        
+
         xml.writeStartElement("output");
             xml.writeTextElement("port", QString::number(
                 midiWorker->portOut));
             xml.writeTextElement("channel", QString::number(
                 midiWorker->channelOut));
         xml.writeEndElement();
-    
+
         xml.writeStartElement("random");
             xml.writeTextElement("tick", QString::number(
                 midiWorker->randomTickAmp));
@@ -473,14 +497,14 @@ void ArpWidget::writeData(QXmlStreamWriter& xml)
             xml.writeTextElement("length", QString::number(
                 midiWorker->randomLengthAmp));
         xml.writeEndElement();
-        
+
         xml.writeStartElement("envelope");
             xml.writeTextElement("attack", QString::number(
                 attackTime->value()));
             xml.writeTextElement("release", QString::number(
                 releaseTime->value()));
         xml.writeEndElement();
-           
+
         xml.writeStartElement("midiControllers");
         for (int l1 = 0; l1 < ccList.count(); l1++) {
             xml.writeStartElement("MIDICC");
@@ -496,45 +520,20 @@ void ArpWidget::writeData(QXmlStreamWriter& xml)
             xml.writeEndElement();
         }
         xml.writeEndElement();
-        
+
     xml.writeEndElement();
 }
 
-void ArpWidget::writeDataText(QTextStream& arpText)
-{
-    arpText << midiWorker->chIn << ' '
-        << midiWorker->repeatPatternThroughChord << '\n';
-    arpText << midiWorker->indexIn[0] << ' ' << midiWorker->indexIn[1] << '\n';
-    arpText << midiWorker->rangeIn[0] << ' ' << midiWorker->rangeIn[1] << '\n';
-    arpText << midiWorker->channelOut << ' ' << midiWorker->portOut << '\n';
-    arpText << midiWorker->randomTickAmp << ' '
-        << midiWorker->randomVelocityAmp << ' '
-        << midiWorker->randomLengthAmp << '\n';
-    arpText << "MIDICC" << endl;
-    for (int l1 = 0; l1 < ccList.count(); l1++) {
-        arpText << ccList.at(l1).ID << ' '
-                << ccList.at(l1).ccnumber << ' '
-                << ccList.at(l1).channel << ' '
-                << ccList.at(l1).min << ' '
-                << ccList.at(l1).max << endl;
-    }
-    arpText << "EOCC" << endl;
-    arpText << "Envelope" << '\n';
-    arpText << attackTime->value() << ' ' << releaseTime->value() << '\n';
-    arpText << midiWorker->pattern << '\n';
-    arpText << "EOP\n"; // End Of Pattern
-    modified = false;
-}                                      
-
 void ArpWidget::readData(QXmlStreamReader& xml)
 {
-    int ctrlID, ccnumber, channel, min, max;
-    
+    int controlID, ccnumber, channel, min, max;
+    int tmp;
+
     while (!xml.atEnd()) {
         xml.readNext();
         if (xml.isEndElement())
             break;
-            
+
         if (xml.isStartElement() && (xml.name() == "pattern")) {
             while (!xml.atEnd()) {
                 xml.readNext();
@@ -557,8 +556,11 @@ void ArpWidget::readData(QXmlStreamReader& xml)
                 xml.readNext();
                 if (xml.isEndElement())
                     break;
-                if (xml.name() == "channel")
-                    chIn->setValue(xml.readElementText().toInt() + 1);
+                if (xml.name() == "channel") {
+                    tmp = xml.readElementText().toInt();
+                    chIn->setCurrentIndex(tmp);
+                    updateChIn(tmp);
+                }
                 else if (xml.name() == "indexMin")
                     indexIn[0]->setValue(xml.readElementText().toInt());
                 else if (xml.name() == "indexMax")
@@ -575,10 +577,16 @@ void ArpWidget::readData(QXmlStreamReader& xml)
                 xml.readNext();
                 if (xml.isEndElement())
                     break;
-                if (xml.name() == "channel")
-                    channelOut->setValue(xml.readElementText().toInt() + 1);
-                else if (xml.name() == "port")
-                    portOut->setValue(xml.readElementText().toInt() + 1);
+                if (xml.name() == "channel") {
+                    tmp = xml.readElementText().toInt();
+                    channelOut->setCurrentIndex(tmp);
+                    updateChannelOut(tmp);
+                }
+                else if (xml.name() == "port") {
+                    tmp = xml.readElementText().toInt();
+                    portOut->setCurrentIndex(tmp);
+                    updatePortOut(tmp);
+                }
                 else skipXmlElement(xml);
             }
         }
@@ -614,7 +622,7 @@ void ArpWidget::readData(QXmlStreamReader& xml)
                 if (xml.isEndElement())
                     break;
                 if (xml.isStartElement() && (xml.name() == "MIDICC")) {
-                    ctrlID = xml.attributes().value("CtrlID").toString().toInt();
+                    controlID = xml.attributes().value("CtrlID").toString().toInt();
                     ccnumber = -1;
                     channel = -1;
                     min = -1;
@@ -634,8 +642,8 @@ void ArpWidget::readData(QXmlStreamReader& xml)
                         else skipXmlElement(xml);
                     }
                     if ((-1 < ccnumber) && (-1 < channel) && (-1 < min) && (-1 < max))
-                        appendMidiCC(ctrlID, ccnumber, channel, min, max);
-                    else qWarning("Controller data incomplete");                  
+                        appendMidiCC(controlID, ccnumber, channel, min, max);
+                    else qWarning("Controller data incomplete");
                 }
                 else skipXmlElement(xml);
             }
@@ -651,10 +659,10 @@ void ArpWidget::skipXmlElement(QXmlStreamReader& xml)
         qWarning("Unknown Element in XML File: %s",qPrintable(xml.name().toString()));
         while (!xml.atEnd()) {
             xml.readNext();
-    
+
             if (xml.isEndElement())
                 break;
-    
+
             if (xml.isStartElement()) {
                 skipXmlElement(xml);
             }
@@ -667,31 +675,31 @@ void ArpWidget::readDataText(QTextStream& arpText)
     QString qs, qs2;
     MidiCC midiCC;
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
-    chIn->setValue(qs2.toInt() + 1);
+    qs2 = qs.section(' ', 0, 0);
+    chIn->setCurrentIndex(qs2.toInt());
     qs2 = qs.section(' ', 1, 1);
     repeatPatternThroughChord->setCurrentIndex(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
+    qs2 = qs.section(' ', 0, 0);
     indexIn[0]->setValue(qs2.toInt());
-    qs2 = qs.section(' ', 1, 1); 
+    qs2 = qs.section(' ', 1, 1);
     indexIn[1]->setValue(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
+    qs2 = qs.section(' ', 0, 0);
     rangeIn[0]->setValue(qs2.toInt());
-    qs2 = qs.section(' ', 1, 1); 
+    qs2 = qs.section(' ', 1, 1);
     rangeIn[1]->setValue(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
-    channelOut->setValue(qs2.toInt() + 1);
-    qs2 = qs.section(' ', 1, 1); 
-    portOut->setValue(qs2.toInt() + 1);
+    qs2 = qs.section(' ', 0, 0);
+    channelOut->setCurrentIndex(qs2.toInt());
+    qs2 = qs.section(' ', 1, 1);
+    portOut->setCurrentIndex(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
+    qs2 = qs.section(' ', 0, 0);
     randomTick->setValue(qs2.toInt());
-    qs2 = qs.section(' ', 1, 1); 
+    qs2 = qs.section(' ', 1, 1);
     randomVelocity->setValue(qs2.toInt());
-    qs2 = qs.section(' ', 2, 2); 
+    qs2 = qs.section(' ', 2, 2);
     randomLength->setValue(qs2.toInt());
     qs = arpText.readLine();
     if (qs == "MIDICC")
@@ -699,7 +707,7 @@ void ArpWidget::readDataText(QTextStream& arpText)
         qs = arpText.readLine();
         while (qs != "EOCC") {
             qs2 = qs.section(' ', 0, 0);
-            int ctrlID = qs2.toInt();
+            int controlID = qs2.toInt();
             qs2 = qs.section(' ', 1, 1);
             int ccnumber = qs2.toInt();
             qs2 = qs.section(' ', 2, 2);
@@ -708,7 +716,7 @@ void ArpWidget::readDataText(QTextStream& arpText)
             int min = qs2.toInt();
             qs2 = qs.section(' ', 4, 4);
             int max = qs2.toInt();
-            appendMidiCC(ctrlID, ccnumber, channel, min, max);
+            appendMidiCC(controlID, ccnumber, channel, min, max);
             qs = arpText.readLine();
         }
     qs = arpText.readLine();
@@ -730,19 +738,19 @@ void ArpWidget::readDataText(QTextStream& arpText)
         }
         qs += '\n' + qs2;
     }
-    patternText->setText(qs);                    
+    patternText->setText(qs);
     modified = false;
-}                                      
+}
 
 void ArpWidget::setChIn(int value)
 {
-    chIn->setValue(value);
+    chIn->setCurrentIndex(value);
     modified = true;
 }
 
 void ArpWidget::setIndexIn(int index, int value)
 {
-    indexIn[index]->setValue(value); 
+    indexIn[index]->setValue(value);
     modified = true;
 }
 
@@ -752,21 +760,8 @@ void ArpWidget::setRangeIn(int index, int value)
     modified = true;
 }
 
-void ArpWidget::setPortOut(int value)
-{
-    portOut->setValue(value);
-    modified = true;
-}
-
-void ArpWidget::setChannelOut(int value)
-{
-
-    channelOut->setValue(value);
-    modified = true;
-}
-
 void ArpWidget::updateText(const QString& newtext)
-{ 
+{
     patternPresetBox->setCurrentIndex(0);
     textRemoveAction->setEnabled(false);
     textStoreAction->setEnabled(true);
@@ -793,7 +788,7 @@ void ArpWidget::loadPatternPresets()
 {
     QString qs;
     QStringList value;
-    
+
     QDir qmahome = QDir(QDir::homePath());
     QString qmarcpath = qmahome.filePath(QMARCNAME);
     QFile f(qmarcpath);
@@ -802,7 +797,7 @@ void ArpWidget::loadPatternPresets()
         QMessageBox::warning(this, PACKAGE,
                 tr("Could not read from resource file"));
         return;
-    }   
+    }
     QTextStream loadText(&f);
     patternNames.clear();
     patternPresets.clear();
@@ -859,7 +854,7 @@ void ArpWidget::checkIfRandomSet()
                 || randomVelocity->value()) {
         randomBox->setFlat(false);
         randomBox->setTitle(tr("Random - ACTIVE"));
-    } 
+    }
     else {
         randomBox->setFlat(true);
         randomBox->setTitle(tr("Random"));
@@ -885,7 +880,7 @@ void ArpWidget::checkIfEnvelopeSet()
     if (attackTime->value() || releaseTime->value()) {
         envelopeBox->setFlat(false);
         envelopeBox->setTitle(tr("Envelope - ACTIVE"));
-    } 
+    }
     else {
         envelopeBox->setFlat(true);
         envelopeBox->setTitle(tr("Envelope"));
@@ -906,7 +901,7 @@ void ArpWidget::storeCurrentPattern()
             tr("New pattern"), QLineEdit::Normal, tr("Arp pattern"), &ok);
 
     if (ok && !qs.isEmpty()) {
-        
+
         emit presetsChanged(qs, patternText->text(), 0);
         patternPresetBox->setCurrentIndex(patternNames.count() - 1);
         textRemoveAction->setEnabled(true);
@@ -937,7 +932,7 @@ void ArpWidget::removeCurrentPattern()
     int currentIndex = patternPresetBox->currentIndex();
     if (currentIndex < 1) {
         return;
-    } 
+    }
 
     qs = tr("Remove \"%1\"?").arg(patternPresetBox->currentText());
 
@@ -947,7 +942,7 @@ void ArpWidget::removeCurrentPattern()
             == QMessageBox::No) {
         return;
     }
-        
+
     emit presetsChanged("", "", currentIndex);
 }
 
@@ -974,6 +969,42 @@ void ArpWidget::setEnvelopeVisible(bool on)
     releaseTime->setVisible(on);
 }
 
+void ArpWidget::setMuted(bool on)
+{
+    midiWorker->setMuted(on);
+    screen->setMuted(on);
+}
+
+void ArpWidget::setLatchMode(bool on)
+{
+    midiWorker->setLatchMode(on);
+    modified = true;
+}
+
+void ArpWidget::setPortOut(int value)
+{
+    portOut->setCurrentIndex(value);
+    modified = true;
+}
+
+void ArpWidget::setChannelOut(int value)
+{
+    channelOut->setCurrentIndex(value);
+    modified = true;
+}
+
+void ArpWidget::updatePortOut(int value)
+{
+    midiWorker->portOut = value;
+    modified = true;
+}
+
+void ArpWidget::updateChannelOut(int value)
+{
+    midiWorker->channelOut = value;
+    modified = true;
+}
+
 bool ArpWidget::isModified()
 {
     return modified;
@@ -995,47 +1026,41 @@ void ArpWidget::moduleDelete()
             == QMessageBox::No) {
         return;
     }
-    emit arpRemove(ID);
+    emit moduleRemove(ID);
 }
 
 void ArpWidget::moduleRename()
 {
     QString newname, oldname;
     bool ok;
-    
+
     oldname = name;
 
     newname = QInputDialog::getText(this, APP_NAME,
                 tr("New Name"), QLineEdit::Normal, oldname.mid(4), &ok);
-                
+
     if (ok && !newname.isEmpty()) {
         name = "Arp:" + newname;
         emit dockRename(name, parentDockID);
     }
 }
 
-void ArpWidget::appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int max)
+void ArpWidget::appendMidiCC(int controlID, int ccnumber, int channel, int min, int max)
 {
     MidiCC midiCC;
     int l1 = 0;
-    switch (ctrlID) {
-        case 0: midiCC.name = "MuteToggle";
-        break;
-        case 1: midiCC.name = "PresetSwitch";
-        break;
-        default: midiCC.name = "Unknown";
-    }
-    midiCC.ID = ctrlID;
+    midiCC.name = midiCCNames.at(controlID);
+    midiCC.ID = controlID;
     midiCC.ccnumber = ccnumber;
     midiCC.channel = channel;
     midiCC.min = min;
     midiCC.max = max;
-    
-    while ( (l1 < ccList.count()) && 
-        ((ctrlID != ccList.at(l1).ID) || 
+
+    while ( (l1 < ccList.count()) &&
+        ((controlID != ccList.at(l1).ID) ||
         (ccnumber != ccList.at(l1).ccnumber) ||
         (channel != ccList.at(l1).channel)) ) l1++;
-    
+
     if (ccList.count() == l1) {
         ccList.append(midiCC);
         qWarning("MIDI Controller %d appended for %s"
@@ -1045,17 +1070,17 @@ void ArpWidget::appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int
         qWarning("MIDI Controller %d already attributed to %s"
                 , ccnumber, qPrintable(midiCC.name));
     }
-        
+
     cancelMidiLearnAction->setEnabled(false);
     modified = true;
 }
 
-void ArpWidget::removeMidiCC(int ctrlID, int ccnumber, int channel)
+void ArpWidget::removeMidiCC(int controlID, int ccnumber, int channel)
 {
     for (int l1 = 0; l1 < ccList.count(); l1++) {
-        if (ccList.at(l1).ID == ctrlID) {
+        if (ccList.at(l1).ID == controlID) {
             if (((ccList.at(l1).ccnumber == ccnumber)
-                    && (ccList.at(l1).channel == channel)) 
+                    && (ccList.at(l1).channel == channel))
                     || (0 > channel)) {
                 ccList.remove(l1);
                 l1--;
@@ -1066,33 +1091,21 @@ void ArpWidget::removeMidiCC(int ctrlID, int ccnumber, int channel)
     modified = true;
 }
 
-void ArpWidget::midiLearnMute()
+void ArpWidget::midiLearn(int controlID)
 {
-    emit setMidiLearn(parentDockID, ID, 0);
+    emit setMidiLearn(parentDockID, ID, controlID);
+    qWarning("Requesting Midi Learn for %s", qPrintable(midiCCNames.at(controlID)));
     cancelMidiLearnAction->setEnabled(true);
-    qWarning("Requesting Midi Learn for MuteToggle");
 }
 
-void ArpWidget::midiForgetMute()
+void ArpWidget::midiForget(int controlID)
 {
-    removeMidiCC(0, 0, -1);
-}
-
-void ArpWidget::midiLearnPresetSwitch()
-{
-    emit setMidiLearn(parentDockID, ID, 1);
-    cancelMidiLearnAction->setEnabled(true);
-    qWarning("Requesting Midi Learn for PresetSwitch");
-}
-
-void ArpWidget::midiForgetPresetSwitch()
-{
-    removeMidiCC(1, 0, -1);
+    removeMidiCC(controlID, 0, -1);
 }
 
 void ArpWidget::midiLearnCancel()
 {
     emit setMidiLearn(parentDockID, ID, -1);
-    cancelMidiLearnAction->setEnabled(false);
     qWarning("Cancelling Midi Learn request");
+    cancelMidiLearnAction->setEnabled(false);
 }
diff --git a/src/arpwidget.h b/src/arpwidget.h
index 4a973d7..b3753c5 100644
--- a/src/arpwidget.h
+++ b/src/arpwidget.h
@@ -1,6 +1,31 @@
+/*!
+ * @file arpwidget.h
+ * @brief Member definitions for the ArpWidget GUI class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
 #ifndef ARPWIDGET_H
 #define ARPWIDGET_H
 
+#include <QSignalMapper>
 #include <QString>
 #include <QTextStream>
 #include <QToolButton>
@@ -19,28 +44,42 @@
 #include "arpscreen.h"
 
 #ifndef MIDICC_H
+
+/*! @brief Structure holding all elements of a MIDI controller allocated to
+ * a QMidiArp parameter assigned via MIDI learn
+ */
 struct MidiCC {
-        QString name;
-        int min;
-        int max;
-        int ccnumber;
-        int channel;
-        int ID;
-    };    
+        QString name;   /**< @brief Name of the assigned parameter (GUI element)*/
+        int min;        /**< @brief Value output when the CC value is 0 */
+        int max;        /**< @brief Value output when the CC value is 127 */
+        int ccnumber;   /**< @brief MIDI CC number of the assigned controller event */
+        int channel;    /**< @brief MIDI channel on which the controller has to come in */
+        int ID;         /**< @brief Internal ID of the assigned parameter (GUI element)*/
+    };
 #define MIDICC_H
 #endif
 
+/*! @brief GUI class associated with and controlling a MidiArp worker
+ *
+ * It controls the MidiArp arpeggiator and
+ * is created alongwith each MidiArp and embedded in a DockWidget on
+ * MainWindow level. It can read its parameter set from an XML stream
+ * by calling its readData member. It manages a ArpWidget::ccList
+ * for each
+ * instance for MIDI controllers attributed through the MIDILearn
+ * context menu. It instantiates a ArpScreen and interacts with it.
+class ArpWidget : public QWidget
+*/
 class ArpWidget : public QWidget
-
 {
   Q_OBJECT
 
   private:
-    QSpinBox *chIn;                        // Channel of input events
+    QComboBox *chIn;                        // Channel of input events
     QSpinBox *indexIn[2];                  // Index input
     QSpinBox *rangeIn[2];                  // Parameter that is mapped, [0] low, [1] high boundary
     QLabel *rangeInLabel, *indexInLabel;
-    QSpinBox *channelOut, *portOut;        // Output channel / port (ALSA Sequencer)
+    QComboBox *channelOut, *portOut;        // Output channel / port (ALSA Sequencer)
     QComboBox *repeatPatternThroughChord;
     QComboBox *triggerMode;
     QComboBox *patternPresetBox;
@@ -51,53 +90,140 @@ class ArpWidget : public QWidget
     QAction *latchModeAction;
     QAction *deleteAction, *renameAction;
     QAction *cancelMidiLearnAction;
+    QSignalMapper *learnSignalMapper, *forgetSignalMapper;
+    QStringList midiCCNames;
     MidiArp *midiWorker;
     QLineEdit *patternText;
     Slider *randomVelocity, *randomTick, *randomLength;
     Slider *attackTime, *releaseTime;
-    bool modified;
+    bool modified;      /**< @brief Is set to True if unsaved parameter modifications exist */
+/*!
+* @brief This function allows ignoring one XML element in the XML stream
+* passed by the caller.
+*
+* It also advances the stream read-in. It is used to
+* ignore unknown elements for both-ways-compatibility
+*
+* @param xml reference to QXmlStreamReader containing the open XML stream
+*/
+    void skipXmlElement(QXmlStreamReader& xml);
+    void loadPatternPresets();
 
+/* PUBLIC MEMBERS */
   public:
-    QString name;
-    int ID, parentDockID;
+/*!
+ * @brief Constructor for ArpWidget. It creates the GUI and an ArpScreen
+ * instance.
+ *
+ * @param p_midiWorker Associated MidiArp Object
+ * @param portCount Number of available ALSA MIDI output ports
+ * @param compactStyle If set to True, Widget will use reduced spacing and small fonts
+ * @param parent The parent widget of this module, i.e. MainWindow
+ */
+    ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QWidget* parent=0);
+    ~ArpWidget();
+
+    QString name;       /**< @brief The name of this ArpWidget as shown in the DockWidget TitleBar */
+    int ID;                     /**< @brief Corresponds to the Engine::midiArpList index of the associated MidiArp */
+    int parentDockID;           /**< @brief The index of the ArpWidget's parent DockWidget in Engine::moduleWindowList */
+    QVector<MidiCC> ccList;     /**< @brief Contains MIDI controller - GUI element bindings */
+
     ArpScreen *screen;
     QStringList patternPresets, patternNames;
     QCheckBox *muteOut;
 
-  public:
-    ArpWidget(MidiArp *p_midiWorker, int portCount, bool compactStyle, QWidget* parent=0);
-    ~ArpWidget();
-    MidiArp *getMidiWorker();
-    
+    void setChIn(int value);
+    void setIndexIn(int index, int value);
+    void setRangeIn(int index, int value);
+
+    //these members are common to all modules
+/*!
+* @brief This function reads all parameters of this module from an XML stream
+* passed by the caller, i.e. MainWindow.
+*
+* @param xml QXmlStreamWriter to read from
+*/
     void readData(QXmlStreamReader& xml);
+/*!
+* @brief This function reads all module parameters of this module from an old
+* QMidiArp .qma text stream.
+*
+* @param arpText QTextStream to read from
+*/
     void readDataText(QTextStream& arpText);
+/*!
+* @brief This function writes all parameters of this module to an XML stream
+* passed by the caller, i.e. MainWindow.
+*
+* @param xml QXmlStreamWriter to write to
+*/
     void writeData(QXmlStreamWriter& xml);
-    void writeDataText(QTextStream& arpText);
-    void skipXmlElement(QXmlStreamReader& xml);
-    void setChIn(int value);
-    void setIndexIn(int index, int value);
+/*!
+* @brief This function returns the MidiArp instance associated with this GUI
+* Widget.
+* @return MidiArp instance associated with this GUI
+*/
+    MidiArp *getMidiWorker();
+/*!
+* @brief Settor for the ArpWidget::channelOut spinbox setting the output
+* channel of this module.
+* @param value Number of the output channel to send data to
+*
+*/
     void setChannelOut(int value);
+/*!
+* @brief Settor for the ArpWidget::portOut spinbox setting the output
+* port of this module.
+* @param value Number of the output port to send data to
+*
+*/
     void setPortOut(int value);
-    void setRangeIn(int index, int value);
-    void loadPatternPresets();
+/*!
+* @brief Accessor for ArpWidget::modified.
+* @return True if unsaved parameter modifications exist
+*
+*/
     bool isModified();
+/*!
+* @brief This function sets ArpWidget::modified.
+* @param m Set to True if unsaved parameter modifications appear
+*
+*/
     void setModified(bool);
-    QVector<MidiCC> ccList;
-      
+
+/* SIGNALS */
   signals:
-    void presetsChanged(const QString&, const QString&, int); 
-                    //int 0 for pattern to append
-                    //or index>0 for pattern to remove
-    void arpRemove(int ID);
-    void dockRename(const QString& name, int parentDockID);
+/*! @brief Emitted to MainWindow::updatePatternPresets saving and deploying modified preset
+ *  list.
+ *  @param pname Name of the modified pattern
+ *  @param pattern Text chain of the modified pattern
+ *  @param index Set to the index of the pattern for removal, or to zero for appending a pattern
+ * */
+    void presetsChanged(const QString& pname, const QString& pattern, int index);
+/*! @brief Emitted to MainWindow::removeLfo for module deletion.
+ *  @param ID The internal ArpWidget::ID of the module to be removed
+ * */
+/*! Emitted to MainWindow::removeSeq for module deletion. */
+    void moduleRemove(int ID);
+/*! @brief Emitted to MainWindow::renameDock for module rename.
+ *  @param mname New name of the module
+ *  @param parentDockID SeqWidget::parentDockID of the module to rename
+ * */
+    void dockRename(const QString& mname, int parentDockID);
+/*! @brief Emitted to Engine::setMidiLearn to listen for incoming events.
+ *  @param parentDockID SeqWidget::parentDockID of the module to rename
+ *  @param ID SeqWidget::ID of the module receiving the MIDI controller
+ *  @param controlID ID of the GUI element to be assigned to the controller
+ *  */
     void setMidiLearn(int parentDockID, int ID, int controlID);
-    
+/*! @brief Forwarded context menu action by signalMapper to call MIDI-Learn/Forget functions.
+ *  @param controlID ID of the GUI element requesting the MIDI controller
+ *  */
+    void triggered(int controlID);
+
+/* PUBLIC SLOTS */
   public slots:
-    void updateChIn(int value);
-    void updateIndexIn(int value);
-    void updateRangeIn(int value);
-    void checkIfInputFilterSet();
-    void updateChannelOut(int value);
+    //these slots are specific for the Arp module
     void updateRandomVelocityAmp(int value);
     void updateRandomTickAmp(int value);
     void updateRandomLengthAmp(int value);
@@ -105,8 +231,7 @@ class ArpWidget : public QWidget
     void updateReleaseTime(int value);
     void checkIfRandomSet();
     void checkIfEnvelopeSet();
-    void setMuted(bool on);
-    void updatePortOut(int value);
+    void checkIfInputFilterSet();
     void updateText(const QString& newtext);
     void updateRepeatPattern(int);
     void updateTriggerMode(int);
@@ -118,15 +243,113 @@ class ArpWidget : public QWidget
     void setRandomVisible(bool on);
     void setEnvelopeVisible(bool on);
     void setInputFilterVisible(bool on);
+
+    void updateChIn(int value);
+    void updateIndexIn(int value);
+    void updateRangeIn(int value);
+
+ /*! @brief Slot for ArpWidget::latchModeAction.
+  * Will cause notes remaining in MidiArp::latchBuffer until new
+  * stakato note received */
+    void setLatchMode(bool);
+
+    //these slots are common to all modules
+/*!
+ *  @brief Slot for the ArpWidget::channelOut spinbox setting the output
+ *  channel of this module.
+ *  @param value Number of the output channel to send data to
+ *
+ */
+    void updateChannelOut(int value);
+/*!
+* @brief Slot for the ArpWidget::portOut spinbox setting the output
+* port of this module.
+* @param value Number of the output port to send data to
+*
+*/
+    void updatePortOut(int value);
+/*!
+* @brief Slot for the ArpWidget::muteOut checkbox.
+* This function suppresses output of Arp data.
+*
+* It calls
+* MidiArp::setMuted and ArpScreen::setMuted
+* @param on Set to True for muting this module
+*
+*/
+    void setMuted(bool on);
+/*!
+* @brief Slot for ArpWidget::deleteAction.
+*
+* This function displays a warning and then emits the
+* ArpWidget::moduleRemove signal to MainWindow with the module ID as
+* parameter.
+*/
     void moduleDelete();
+/*!
+* @brief Slot for ArpWidget::renameAction.
+*
+* This function queries a new name then emits the ArpWidget::dockRename
+* signal to MainWindow with the new name and the dockWidget ID to rename.
+*/
     void moduleRename();
-    void appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int max);
-    void removeMidiCC(int ctrlID, int ccnumber, int channel);
-    void midiLearnMute();
-    void midiForgetMute();
-    void midiLearnPresetSwitch();
-    void midiForgetPresetSwitch();
+/*!
+* @brief This function appends a new MIDI controller - GUI element
+* binding to ArpWidget::ccList.
+*
+* Before appending, it checks whether this binding already exists.
+* @param controlID The ID of the control GUI element (found
+* in ArpWidget::midiCCNames)
+* @param ccnumber The CC of the MIDI controller to be attributed
+* @param channel The MIDI Channel of the MIDI controller to be attributed
+* @param min The minimum value to which the controller range is mapped
+* @param max The maximum value to which the controller range is mapped
+*/
+    void appendMidiCC(int controlID, int ccnumber, int channel, int min, int max);
+/*!
+* @brief This function removes a MIDI controller - GUI element
+* binding from the ArpWidget::ccList.
+*
+* @param controlID The ID of the control GUI element (found
+* in ArpWidget::midiCCNames)
+* @param ccnumber The CC of the MIDI controller to be removed
+* @param channel The MIDI Channel of the MIDI controller to be removed
+*/
+    void removeMidiCC(int controlID, int ccnumber, int channel);
+/*!
+* @brief Slot for ArpWidget::triggered signal created by MIDI-Learn context
+* menu MIDI Learn action.
+*
+* This function sets Engine into
+* MIDI Learn status for this module and controlID.
+* It emits ArpWidget::setMidiLearn with the necessary module and GUI element
+* information parameters.
+* Engine will then wait for an incoming controller event and trigger the
+* attribution by calling appendMidiCC.
+*
+* @param controlID The ID of the control GUI element (found
+* in ArpWidget::midiCCNames)
+*/
+    void midiLearn(int controlID);
+/*!
+* @brief Slot for ArpWidget::triggered signal created by a
+* MIDI-Learn context menu Forget action.
+*
+* This function removes a controller
+* binding attribution by calling removeMidiCC.
+*
+* @param controlID The ID of the control GUI element (found
+* in ArpWidget::midiCCNames)
+*/
+    void midiForget(int controlID);
+/*!
+* @brief Slot for ArpWidget::cancelMidiLearnAction in MIDI-Learn
+* context menu. This function signals cancellation of the
+* MIDI-Learn Process to Engine.
+*
+* It emits ArpWidget::setMidiLearn with controlID set to -1 meaning cancel.
+*/
     void midiLearnCancel();
 };
-  
+
 #endif
diff --git a/src/arpdata.cpp b/src/engine.cpp
similarity index 68%
rename from src/arpdata.cpp
rename to src/engine.cpp
index c2927cf..c1ec038 100644
--- a/src/arpdata.cpp
+++ b/src/engine.cpp
@@ -1,36 +1,61 @@
+/**
+ * @file engine.cpp
+ * @brief Implements the Engine module management class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
+
 #include <QString>
-#include <QFile>
-#include <QFileDialog>
-#include <QTextStream>
-#include <QMessageBox>
 
 #include "seqdriver.h"
-#include "arpdata.h"
+#include "engine.h"
 
 
-ArpData::ArpData(QWidget *parent) : QWidget(parent), modified(false)
+Engine::Engine(int p_portCount, QWidget *parent) : QWidget(parent), modified(false)
 {
-    seqDriver = new SeqDriver(&midiArpList, &midiLfoList, &midiSeqList, this);
-    connect(seqDriver, SIGNAL(controlEvent(int, int, int)), 
+    portCount = p_portCount;
+    seqDriver = new SeqDriver(&midiArpList, &midiLfoList, &midiSeqList, portCount, this);
+    connect(seqDriver, SIGNAL(controlEvent(int, int, int)),
             this, SLOT(handleController(int, int, int)));
     midiLearnFlag = false;
+    grooveTick = 0;
+    grooveVelocity = 0;
+    grooveLength = 0;
 }
 
-ArpData::~ArpData(){
+Engine::~Engine(){
 }
 //Arp handling
-void ArpData::addMidiArp(MidiArp *midiArp)
+void Engine::addMidiArp(MidiArp *midiArp)
 {
     midiArpList.append(midiArp);
 }
 
-void ArpData::addArpWidget(ArpWidget *arpWidget)
+void Engine::addArpWidget(ArpWidget *arpWidget)
 {
     arpWidgetList.append(arpWidget);
     modified = true;
 }
 
-void ArpData::removeMidiArp(MidiArp *midiArp)
+void Engine::removeMidiArp(MidiArp *midiArp)
 {
     if (seqDriver->runArp && (moduleWindowCount() < 1)) {
         seqDriver->setQueueStatus(false);
@@ -40,14 +65,14 @@ void ArpData::removeMidiArp(MidiArp *midiArp)
         delete midiArpList.takeAt(i);
 }
 
-void ArpData::removeArpWidget(ArpWidget *arpWidget)
+void Engine::removeArpWidget(ArpWidget *arpWidget)
 {
     removeMidiArp(arpWidget->getMidiWorker());
     arpWidgetList.removeOne(arpWidget);
     modified = true;
 }
 
-void ArpData::updatePatternPresets(const QString& n, const QString& p, int index)
+void Engine::updatePatternPresets(const QString& n, const QString& p, int index)
 {
     int l1;
     for (l1 = 0; l1 < midiArpCount(); l1++) {
@@ -55,40 +80,70 @@ void ArpData::updatePatternPresets(const QString& n, const QString& p, int index
     }
 }
 
-int ArpData::midiArpCount()
+int Engine::midiArpCount()
 {
     return(midiArpList.count());
 }
 
-int ArpData::arpWidgetCount()
+int Engine::arpWidgetCount()
 {
     return(arpWidgetList.count());
 }
 
-MidiArp *ArpData::midiArp(int index)
+MidiArp *Engine::midiArp(int index)
 {
     return(midiArpList.at(index));
 }
 
-ArpWidget *ArpData::arpWidget(int index)
+ArpWidget *Engine::arpWidget(int index)
 {
     return(arpWidgetList.at(index));
 }
 
+
+void Engine::setGrooveTick(int val)
+{
+    grooveTick = val;
+    sendGroove();
+    modified = true;
+}
+
+void Engine::setGrooveVelocity(int val)
+{
+    grooveVelocity = val;
+    sendGroove();
+    modified = true;
+}
+
+void Engine::setGrooveLength(int val)
+{
+    grooveLength = val;
+    sendGroove();
+    modified = true;
+}
+
+void Engine::sendGroove()
+{
+    for (int l1 = 0; l1 < midiArpList.count(); l1++) {
+        midiArpList.at(l1)->newGrooveValues(grooveTick, grooveVelocity,
+                grooveLength);
+    }
+}
+
 //LFO handling
 
-void ArpData::addMidiLfo(MidiLfo *midiLfo)
+void Engine::addMidiLfo(MidiLfo *midiLfo)
 {
     midiLfoList.append(midiLfo);
 }
 
-void ArpData::addLfoWidget(LfoWidget *lfoWidget)
+void Engine::addLfoWidget(LfoWidget *lfoWidget)
 {
     lfoWidgetList.append(lfoWidget);
     modified = true;
 }
 
-void ArpData::removeMidiLfo(MidiLfo *midiLfo)
+void Engine::removeMidiLfo(MidiLfo *midiLfo)
 {
     if (seqDriver->runArp && (moduleWindowCount() < 1)) {
         seqDriver->setQueueStatus(false);
@@ -98,47 +153,47 @@ void ArpData::removeMidiLfo(MidiLfo *midiLfo)
         delete midiLfoList.takeAt(i);
 }
 
-void ArpData::removeLfoWidget(LfoWidget *lfoWidget)
+void Engine::removeLfoWidget(LfoWidget *lfoWidget)
 {
     removeMidiLfo(lfoWidget->getMidiWorker());
     lfoWidgetList.removeOne(lfoWidget);
     modified = true;
 }
 
-int ArpData::midiLfoCount()
+int Engine::midiLfoCount()
 {
     return(midiLfoList.count());
 }
 
-int ArpData::lfoWidgetCount()
+int Engine::lfoWidgetCount()
 {
     return(lfoWidgetList.count());
 }
 
-MidiLfo *ArpData::midiLfo(int index)
+MidiLfo *Engine::midiLfo(int index)
 {
     return(midiLfoList.at(index));
 }
 
-LfoWidget *ArpData::lfoWidget(int index)
+LfoWidget *Engine::lfoWidget(int index)
 {
     return(lfoWidgetList.at(index));
 }
 
 //SEQ handling
 
-void ArpData::addMidiSeq(MidiSeq *midiSeq)
+void Engine::addMidiSeq(MidiSeq *midiSeq)
 {
     midiSeqList.append(midiSeq);
 }
 
-void ArpData::addSeqWidget(SeqWidget *seqWidget)
+void Engine::addSeqWidget(SeqWidget *seqWidget)
 {
     seqWidgetList.append(seqWidget);
     modified = true;
 }
 
-void ArpData::removeMidiSeq(MidiSeq *midiSeq)
+void Engine::removeMidiSeq(MidiSeq *midiSeq)
 {
     if (seqDriver->runArp && (moduleWindowCount() < 1)) {
         seqDriver->setQueueStatus(false);
@@ -148,59 +203,59 @@ void ArpData::removeMidiSeq(MidiSeq *midiSeq)
         delete midiSeqList.takeAt(i);
 }
 
-void ArpData::removeSeqWidget(SeqWidget *seqWidget)
+void Engine::removeSeqWidget(SeqWidget *seqWidget)
 {
     removeMidiSeq(seqWidget->getMidiWorker());
     seqWidgetList.removeOne(seqWidget);
     modified = true;
 }
 
-int ArpData::midiSeqCount()
+int Engine::midiSeqCount()
 {
     return(midiSeqList.count());
 }
 
-int ArpData::seqWidgetCount()
+int Engine::seqWidgetCount()
 {
     return(seqWidgetList.count());
 }
 
-MidiSeq *ArpData::midiSeq(int index)
+MidiSeq *Engine::midiSeq(int index)
 {
     return(midiSeqList.at(index));
 }
 
-SeqWidget *ArpData::seqWidget(int index)
+SeqWidget *Engine::seqWidget(int index)
 {
     return(seqWidgetList.at(index));
 }
 
 //module Window handling (dockWidgets)
 
-void ArpData::addModuleWindow(QDockWidget *moduleWindow)
+void Engine::addModuleWindow(QDockWidget *moduleWindow)
 {
     moduleWindowList.append(moduleWindow);
     modified = true;
 }
 
-void ArpData::removeModuleWindow(QDockWidget *moduleWindow)
+void Engine::removeModuleWindow(QDockWidget *moduleWindow)
 {
     moduleWindowList.removeOne(moduleWindow);
     delete moduleWindow;
     modified = true;
 }
 
-QDockWidget *ArpData::moduleWindow(int index)
+QDockWidget *Engine::moduleWindow(int index)
 {
     return(moduleWindowList.at(index));
 }
 
-int ArpData::moduleWindowCount()
+int Engine::moduleWindowCount()
 {
     return(moduleWindowList.count());
 }
 
-void ArpData::updateIDs(int curID)
+void Engine::updateIDs(int curID)
 {
     int l1, tempDockID;
     for (l1 = 0; l1 < arpWidgetCount(); l1++) {
@@ -228,7 +283,7 @@ void ArpData::updateIDs(int curID)
 
 //general
 
-bool ArpData::isModified()
+bool Engine::isModified()
 {
     bool arpmodified = false;
     bool lfomodified = false;
@@ -244,18 +299,18 @@ bool ArpData::isModified()
             lfomodified = true;
             break;
         }
-        
+
     for (int l1 = 0; l1 < seqWidgetCount(); l1++)
         if (seqWidget(l1)->isModified()) {
             seqmodified = true;
             break;
         }
 
-    return modified || seqDriver->isModified() 
+    return modified || seqDriver->isModified()
                     || arpmodified || lfomodified || seqmodified;
 }
 
-void ArpData::setModified(bool m)
+void Engine::setModified(bool m)
 {
     modified = m;
     seqDriver->setModified(m);
@@ -270,30 +325,23 @@ void ArpData::setModified(bool m)
         seqWidget(l1)->setModified(m);
 }
 
-void ArpData::registerPorts(int num)
-{
-    portCount = num;
-    seqDriver->registerPorts(num);
-}
-
-int ArpData::getPortCount()
+int Engine::getPortCount()
 {
     return(portCount);
 }
 
-void ArpData::runQueue(bool on)
+void Engine::runQueue(bool on)
 {
-    seqDriver->runQueue(on);
     if (midiArpList.count() > 0)
         seqDriver->setQueueStatus(on);
 }
 
-int ArpData::getAlsaClientId()
+int Engine::getAlsaClientId()
 {
     return seqDriver->getAlsaClientId();
 }
 
-void ArpData::handleController(int ccnumber, int channel, int value)
+void Engine::handleController(int ccnumber, int channel, int value)
 {
     bool m;
     int min, max, sval;
@@ -314,7 +362,7 @@ void ArpData::handleController(int ccnumber, int channel, int value)
                                         arpWidget(l1)->muteOut->setChecked(!m);
                                         return;
                                     }
-                        case 1: 
+                        case 1:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 arpWidget(l1)->selectPatternPreset(sval);
@@ -336,7 +384,7 @@ void ArpData::handleController(int ccnumber, int channel, int value)
                 }
             }
         }
-    
+
         for (int l1 = 0; l1 < lfoWidgetCount(); l1++) {
             cclist = lfoWidget(l1)->ccList;
             for (int l2 = 0; l2 < cclist.count(); l2++) {
@@ -361,41 +409,58 @@ void ArpData::handleController(int ccnumber, int channel, int value)
                                     }
                                 }
                         break;
-                        
-                        case 1: 
+
+                        case 1:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 lfoWidget(l1)->amplitude->setValue(sval);
                                 return;
                         break;
-                        
-                        case 2: 
+
+                        case 2:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 lfoWidget(l1)->offset->setValue(sval);
                                 return;
                         break;
-                        case 3: 
+                        case 3:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 lfoWidget(l1)->waveFormBox->setCurrentIndex(sval);
                                 lfoWidget(l1)->updateWaveForm(sval);
                                 return;
                         break;
-                        case 4: 
+                        case 4:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 lfoWidget(l1)->freqBox->setCurrentIndex(sval);
                                 lfoWidget(l1)->updateFreq(sval);
                                 return;
                         break;
+                        case 5: if (min == max) {
+                                    if (value == max) {
+                                        m = lfoWidget(l1)->recordAction->isChecked();
+                                        lfoWidget(l1)->recordAction->setChecked(!m);
+                                        return;
+                                    }
+                                }
+                                else {
+                                    if (value == max) {
+                                        lfoWidget(l1)->recordAction->setChecked(true);
+                                    }
+                                    if (value == min) {
+                                        lfoWidget(l1)->recordAction->setChecked(false);
+                                    }
+                                }
+                        break;
+
                         default:
                         break;
                     }
                 }
             }
         }
-        
+
         for (int l1 = 0; l1 < seqWidgetCount(); l1++) {
             cclist = seqWidget(l1)->ccList;
             for (int l2 = 0; l2 < cclist.count(); l2++) {
@@ -421,19 +486,37 @@ void ArpData::handleController(int ccnumber, int channel, int value)
                                 }
                         break;
 
-                        case 1: 
+                        case 1:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 seqWidget(l1)->velocity->setValue(sval);
                                 return;
                         break;
-                        
-                        case 2: 
+
+                        case 2:
                                 sval = min + ((double)value * (max - min)
                                         / 127);
                                 seqWidget(l1)->notelength->setValue(sval);
                                 return;
                         break;
+
+                        case 3: if (min == max) {
+                                    if (value == max) {
+                                        m = seqWidget(l1)->recordAction->isChecked();
+                                        seqWidget(l1)->recordAction->setChecked(!m);
+                                        return;
+                                    }
+                                }
+                                else {
+                                    if (value == max) {
+                                        seqWidget(l1)->recordAction->setChecked(true);
+                                    }
+                                    if (value == min) {
+                                        seqWidget(l1)->recordAction->setChecked(false);
+                                    }
+                                }
+                        break;
+
                         default:
                         break;
                     }
@@ -455,12 +538,12 @@ void ArpData::handleController(int ccnumber, int channel, int value)
             seqWidget(midiLearnModuleID)->appendMidiCC(midiLearnID,
                     ccnumber, channel, min, 127);
         }
-        
+
         midiLearnFlag = false;
     }
 }
 
-void ArpData::setMidiLearn(int moduleWindowID, int moduleID, int controlID)
+void Engine::setMidiLearn(int moduleWindowID, int moduleID, int controlID)
 {
     if (0 > controlID) {
         midiLearnFlag = false;
@@ -474,7 +557,7 @@ void ArpData::setMidiLearn(int moduleWindowID, int moduleID, int controlID)
     }
 }
 
-void ArpData::setCompactStyle(bool on)
+void Engine::setCompactStyle(bool on)
 {
     int l1;
     if (on) {
diff --git a/src/engine.h b/src/engine.h
new file mode 100644
index 0000000..bf8ad02
--- /dev/null
+++ b/src/engine.h
@@ -0,0 +1,133 @@
+/**
+ * @file engine.h
+ * @brief Member definitions for the Engine module management class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
+#ifndef ENGINE_H
+#define ENGINE_H
+
+#include <QWidget>
+#include <QDockWidget>
+#include <QList>
+#include "seqdriver.h"
+#include "midiarp.h"
+#include "arpwidget.h"
+#include "midilfo.h"
+#include "lfowidget.h"
+#include "midiseq.h"
+#include "seqwidget.h"
+
+/*!
+ * @brief Manages created module components in lists. Instantiates SeqDriver.
+ *
+ * For each module type there is a QList for each of
+ * its components (for example MidiArp and ArpWidget). In parallel there is
+ * a common list for all modules containing their DockWidgets.
+ * Engine also instantiates the SeqDriver MIDI backend and handles MIDI
+ * controller events through signaling by SeqDriver. Controllers are
+ * dispatched to the modules as requiered by their MIDI Learn
+ * MidiCCList.
+ *
+ */
+class Engine : public QWidget  {
+
+  Q_OBJECT
+
+  private:
+    QList<MidiArp *> midiArpList;
+    QList<ArpWidget *> arpWidgetList;
+    QList<QDockWidget *> moduleWindowList;
+    QList<MidiLfo *> midiLfoList;
+    QList<LfoWidget *> lfoWidgetList;
+    QList<MidiSeq *> midiSeqList;
+    QList<SeqWidget *> seqWidgetList;
+    int portCount;
+    bool modified;
+    int mute_ccnumber, midiLearnID, midiLearnWindowID, midiLearnModuleID;
+    bool midi_mutable, midiLearnFlag;
+
+  public:
+    int grooveTick, grooveVelocity, grooveLength;
+
+  public:
+    SeqDriver *seqDriver;
+
+    Engine(int p_portCount, QWidget* parent=0);
+    ~Engine();
+    int getPortCount();
+    bool isModified();
+
+
+    void addModuleWindow(QDockWidget *moduleWindow);
+    void removeModuleWindow(QDockWidget *moduleWindow);
+    QDockWidget *moduleWindow(int index);
+    int moduleWindowCount();
+    void updateIDs(int curID);
+
+    void addMidiArp(MidiArp *midiArp);
+    void addArpWidget(ArpWidget *arpWidget);
+    void removeMidiArp(MidiArp *midiArp);
+    void removeArpWidget(ArpWidget *arpWidget);
+    int midiArpCount();
+    int arpWidgetCount();
+    MidiArp *midiArp(int index);
+    ArpWidget *arpWidget(int index);
+
+    void addMidiLfo(MidiLfo *midiLfo);
+    void addLfoWidget(LfoWidget *lfoWidget);
+    void removeMidiLfo(MidiLfo *midiLfo);
+    void removeLfoWidget(LfoWidget *lfoWidget);
+    int midiLfoCount();
+    int lfoWidgetCount();
+    MidiLfo *midiLfo(int index);
+    LfoWidget *lfoWidget(int index);
+
+    void addMidiSeq(MidiSeq *midiSeq);
+    void addSeqWidget(SeqWidget *seqWidget);
+    void removeMidiSeq(MidiSeq *midiSeq);
+    void removeSeqWidget(SeqWidget *seqWidget);
+    int midiSeqCount();
+    int seqWidgetCount();
+    MidiSeq *midiSeq(int index);
+    SeqWidget *seqWidget(int index);
+    int getAlsaClientId();
+
+  public slots:
+    void runQueue(bool);
+/**
+ * @brief This function is used to set the modified flag, which is queried before
+ * loading a new session file or quitting qmidiarp.
+ *
+ * @param m Set to True if parameter modifications are present
+ */
+    void setModified(bool);
+    void updatePatternPresets(const QString& n, const QString& p, int index);
+    void handleController(int ccnumber, int channel, int value);
+    void setMidiLearn(int moduleWindowID, int moduleID, int controlID);
+    void setCompactStyle(bool on);
+    void setGrooveTick(int grooveTick);
+    void setGrooveVelocity(int grooveVelocity);
+    void setGrooveLength(int grooveLength);
+    void sendGroove();
+};
+
+#endif
diff --git a/src/groovewidget.cpp b/src/groovewidget.cpp
index b53b709..2ca333b 100644
--- a/src/groovewidget.cpp
+++ b/src/groovewidget.cpp
@@ -1,6 +1,31 @@
+/*!
+ * @file groovewidget.cpp
+ * @brief Implementation of the GrooveWidget QWidget class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
+
 #include <QString>
 #include <QLabel>
-#include <QSlider> 
+#include <QSlider>
 #include <QBoxLayout>
 #include <QPushButton>
 #include <QComboBox>
@@ -35,7 +60,6 @@ GrooveWidget::GrooveWidget(QWidget *parent) : QWidget(parent)
     GrooveWidgetLayout->addWidget(grooveVelocity);
     GrooveWidgetLayout->addWidget(grooveLength);
     GrooveWidgetLayout->addStretch();
-    setMinimumWidth(300);
     setLayout(GrooveWidgetLayout);
 }
 
diff --git a/src/groovewidget.h b/src/groovewidget.h
index 94f284c..2123edb 100644
--- a/src/groovewidget.h
+++ b/src/groovewidget.h
@@ -1,3 +1,27 @@
+/**
+ * @file groovewidget.h
+ * @brief Member definitions for the GrooveWidget QWidget class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
 #ifndef GROOVEWIDGET_H
 #define GROOVEWIDGET_H
 
@@ -10,6 +34,15 @@
 #include <QSpinBox>
 #include "slider.h"
 
+/*!
+ * @brief Creates a QWidget with three sliders controlling the arpeggiator groove.
+ *
+ * The GrooveWidget is instantiated by MainWindow on program start. It is
+ * embedded in a DockWindow and shown/hidden by a MainWindow menu entry and
+ * tool button.
+ * Each Slider controls a groove setting transmitted to Engine at every change.
+ *
+ */
 class GrooveWidget : public QWidget
 
 {
@@ -17,20 +50,20 @@ class GrooveWidget : public QWidget
 
   public:
     Slider *grooveVelocity, *grooveTick, *grooveLength;
-          
+
   public:
     GrooveWidget(QWidget* parent=0);
     ~GrooveWidget();
-    
+
   signals:
     void newGrooveVelocity(int);
     void newGrooveTick(int);
     void newGrooveLength(int);
-            
+
   public slots:
     void updateGrooveVelocity(int);
     void updateGrooveTick(int);
     void updateGrooveLength(int);
 };
-  
+
 #endif
diff --git a/src/jacksync.cpp b/src/jacksync.cpp
index 8b27394..13f742c 100644
--- a/src/jacksync.cpp
+++ b/src/jacksync.cpp
@@ -1,28 +1,33 @@
-//      jacksync.cpp
-//      
-//      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
-//      
-//      This program is free software; you can redistribute it and/or modify
-//      it under the terms of the GNU General Public License as published by
-//      the Free Software Foundation; either version 2 of the License, or
-//      (at your option) any later version.
-//      
-//      This program is distributed in the hope that it will be useful,
-//      but WITHOUT ANY WARRANTY; without even the implied warranty of
-//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//      GNU General Public License for more details.
-//      
-//      You should have received a copy of the GNU General Public License
-//      along with this program; if not, write to the Free Software
-//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-//      MA 02110-1301, USA.
-
+/*!
+ * @file jacksync.cpp
+ * @brief Implements the JackSync QObject class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 
 #include "jacksync.h"
 #include "config.h"
 
 
-JackSync::JackSync() 
+JackSync::JackSync()
 {
     transportState = JackTransportStopped;
     j_frame_time = 0;
@@ -35,7 +40,7 @@ JackSync::~JackSync()
     }
     if (jack_handle != 0) {
         jack_client_close(jack_handle);
-        jack_handle = 0; 
+        jack_handle = 0;
     }
 }
 
@@ -47,27 +52,27 @@ int JackSync::initJack()
     }
 
     jack_on_shutdown(jack_handle, jack_shutdown, (void *)this);
-    
+
     jack_set_sync_callback(jack_handle, sync_callback, (void *)this);
 
     qWarning("jack sync callback registered");
-    
+
     return(0);
 }
- 
-int JackSync::activateJack() 
+
+int JackSync::activateJack()
 {
     if (jack_activate(jack_handle)) {
         qWarning("cannot activate client");
         jackRunning = false;
         return(1);
     }
-    
+
     jackRunning = true;
     return(0);
 }
 
-int JackSync::deactivateJack() 
+int JackSync::deactivateJack()
 {
     if (jackRunning) {
         if (jack_deactivate(jack_handle)) {
@@ -84,12 +89,12 @@ void JackSync::jack_shutdown(void *arg)
 {
     JackSync *rd = (JackSync *) arg;
     rd->setJackRunning(false);
-    
+
     qWarning("JACK shut down. JACK sync Disabled.");
     emit rd->j_shutdown();
 }
 
-int JackSync::sync_callback(jack_transport_state_t state, 
+int JackSync::sync_callback(jack_transport_state_t state,
                             jack_position_t *pos, void *arg)
 {
     JackSync *rd;
@@ -101,21 +106,21 @@ int JackSync::sync_callback(jack_transport_state_t state,
 
 int JackSync::jack_sync(jack_transport_state_t state)
 {
-    
+
     switch (state){
         case JackTransportStopped:
             qWarning( "[JackTransportStopped]" );
         break;
-        
-        case JackTransportRolling:  
+
+        case JackTransportRolling:
             qWarning( "[JackTransportRolling]" );
         break;
-        
+
         case JackTransportStarting:
             emit j_tr_state(true);
             qWarning( "[JackTransportStarting]" );
         break;
-        
+
         case JackTransportLooping:
             qWarning( "[JackTransportLooping]" );
         break;
diff --git a/src/jacksync.h b/src/jacksync.h
index a87e535..d8f17a2 100644
--- a/src/jacksync.h
+++ b/src/jacksync.h
@@ -1,3 +1,27 @@
+/*!
+ * @file jacksync.h
+ * @brief Headers for the JackSync QObject class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef JACKSYNC_H
 #define JACKSYNC_H
 
@@ -5,12 +29,20 @@
 #include <jack/jack.h>
 #include <jack/transport.h>
 
+/*!
+ * The JackSync class is a QObject providing access to the transport status
+ * of the Jack Audio Connection Kit (JACK) transport system. It provides
+ * functions to register and initialise a jack client and to read the
+ * current frame position of a transport master.
+ *
+ * @brief QObject class providing access to jack transport status.
+ */
 class JackSync : public QObject
 {
     Q_OBJECT
 
   private:
-    static int sync_callback(jack_transport_state_t state, 
+    static int sync_callback(jack_transport_state_t state,
                             jack_position_t *pos, void *arg);
     static void jack_shutdown(void *arg);
     int jack_sync(jack_transport_state_t state);
@@ -19,12 +51,12 @@ class JackSync : public QObject
     double j_frame_time;
     jack_client_t *jack_handle;
     jack_position_t current_pos;
-    
-    
+
+
   public:
     JackSync();
     ~JackSync();
-    
+
   signals:
     void j_tr_state(bool on);
     void j_shutdown();
@@ -34,7 +66,7 @@ class JackSync : public QObject
     int initJack();
     int activateJack();
     int deactivateJack();
-     
+
     void setJackRunning(bool on);
     jack_transport_state_t get_state();
     jack_position_t get_pos();
diff --git a/src/lfoscreen.cpp b/src/lfoscreen.cpp
index 7d0bc71..8960ca8 100644
--- a/src/lfoscreen.cpp
+++ b/src/lfoscreen.cpp
@@ -1,35 +1,34 @@
-/*
- *      lfoscreen.cpp
- *      
- *      This file is part of QMidiArp.
- * 
+/*!
+ * @file lfoscreen.cpp
+ * @brief Implementation of the LfoScreen class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
-#include <QPolygon>
 #include <QPainter>
 #include <QPaintDevice>
 #include <QPen>
 #include <QPixmap>
-#include <QBrush>
-#include <QSizePolicy>
-#include <QSize>
 
 #include "lfoscreen.h"
-#include "arpdata.h"
 
 
 LfoScreen::LfoScreen(QWidget* parent) : QWidget (parent)
@@ -37,6 +36,7 @@ LfoScreen::LfoScreen(QWidget* parent) : QWidget (parent)
     setPalette(QPalette(QColor(0, 20, 100), QColor(0, 20, 100)));
     mouseX = 0;
     mouseY = 0;
+    currentIndex = 0;
     isMuted = false;
 }
 
@@ -48,7 +48,6 @@ LfoScreen::~LfoScreen()
 void LfoScreen::paintEvent(QPaintEvent*)
 {
     QPainter p(this);
-    QPolygon points(7);
     QPen pen;
     pen.setWidth(1);
     p.setFont(QFont("Helvetica", 8));
@@ -59,23 +58,29 @@ void LfoScreen::paintEvent(QPaintEvent*)
     int beat = 4;
     int npoints = 0;
     int ypos, xpos, xscale, yscale;
-    int octYoffset;
     w = QWidget::width();
     h = QWidget::height();
     int notestreak_thick = 2;
     int ofs;
     int x, x1;
-    int octave = 0;
     int maxOctave = 1;
     int minOctave = 0;
     int beatRes = 1.0;
     int beatDiv = 0;
     int noctaves = 2;
     l2 = 0;
-    QChar c;
+
+    //Grid
+    if (p_data.isEmpty()) return;
+    nsteps = p_data.at(p_data.count() - 1).tick / TPQN;
+    beatRes = (p_data.count() - 1) / nsteps;
+    beatDiv = (beatRes * nsteps > 64) ? 64 / nsteps : beatRes;
+    npoints = beatRes * nsteps;
+    xscale = (w - 2 * LFOSCR_HMARG) / nsteps;
+    yscale = h - 2 * LFOSCR_VMARG;
 
     //Beryll Filled Frame
-    if (isMuted) 
+    if (isMuted)
         p.fillRect(0, 0, w, h, QColor(70, 70, 70));
     else
         p.fillRect(0, 0, w, h, QColor(50, 10, 10));
@@ -84,37 +89,28 @@ void LfoScreen::paintEvent(QPaintEvent*)
     p.setPen(QColor(160, 20, 20));
     p.drawRect(0, 0, w - 1, h - 1);
 
-    //Grid 
-    if (p_lfoData.isEmpty()) return;
-    nsteps = p_lfoData.at(p_lfoData.count() - 1).tick / TICKS_PER_QUARTER;
-    beatRes = (p_lfoData.count() - 1) / nsteps;
-    beatDiv = (beatRes * nsteps > 64) ? 64 / nsteps : beatRes;
-    npoints = beatRes * nsteps;
-    xscale = (w - 2 * LFOSCREEN_HMARGIN) / nsteps;
-    yscale = h - 2 * LFOSCREEN_VMARGIN;
-
     //Beat separators
     for (l1 = 0; l1 < nsteps + 1; l1++) {
 
         if (l1 < 10) {
-            ofs = w / nsteps * .5 - 4 + LFOSCREEN_HMARGIN;
+            ofs = w / nsteps * .5 - 4 + LFOSCR_HMARG;
         } else {
-            ofs = w / nsteps * .5 - 6 + LFOSCREEN_HMARGIN;
+            ofs = w / nsteps * .5 - 6 + LFOSCR_HMARG;
         }
         if ((bool)(l1%beat)) {
             p.setPen(QColor(180, 100, 60));
         } else {
-            p.setPen(QColor(180, 100, 100));      
+            p.setPen(QColor(180, 100, 100));
         }
         x = l1 * xscale;
-            p.drawLine(LFOSCREEN_HMARGIN + x, LFOSCREEN_VMARGIN,
-                LFOSCREEN_HMARGIN + x, h-LFOSCREEN_VMARGIN);
+            p.drawLine(LFOSCR_HMARG + x, LFOSCR_VMARG,
+                LFOSCR_HMARG + x, h-LFOSCR_VMARG);
 
         if (l1 < nsteps) {
-            p.setPen(QColor(180, 150, 100));      
+            p.setPen(QColor(180, 150, 100));
 
             //Beat numbers
-            p.drawText(ofs + x, LFOSCREEN_VMARGIN, QString::number(l1+1));
+            p.drawText(ofs + x, LFOSCR_VMARG, QString::number(l1+1));
 
             // Beat divisor separators
             p.setPen(QColor(120, 60, 20));
@@ -122,10 +118,10 @@ void LfoScreen::paintEvent(QPaintEvent*)
             for (l2 = 1; l2 < beatDiv; l2++) {
                 x1 = x + l2 * xscale / beatDiv;
                 if (x1 < xscale * nsteps)
-                    p.drawLine(LFOSCREEN_HMARGIN + x1,
-                            LFOSCREEN_VMARGIN, LFOSCREEN_HMARGIN + x1,
-                            h - LFOSCREEN_VMARGIN);
-            } 
+                    p.drawLine(LFOSCR_HMARG + x1,
+                            LFOSCR_VMARG, LFOSCR_HMARG + x1,
+                            h - LFOSCR_VMARG);
+            }
         }
     }
 
@@ -133,62 +129,61 @@ void LfoScreen::paintEvent(QPaintEvent*)
     p.setPen(QColor(180, 120, 40));
     noctaves = maxOctave - minOctave + 1;
     for (l1 = 0; l1 < noctaves + 2; l1++) {
-        ypos = yscale * l1 / noctaves + LFOSCREEN_VMARGIN;
-        p.drawLine(LFOSCREEN_HMARGIN, ypos, w - LFOSCREEN_HMARGIN, ypos);        
-        p.drawText(1, 
-                yscale * (l1) / noctaves + LFOSCREEN_VMARGIN + 4, 
+        ypos = yscale * l1 / noctaves + LFOSCR_VMARG;
+        p.drawLine(LFOSCR_HMARG, ypos, w - LFOSCR_HMARG, ypos);
+        p.drawText(1,
+                yscale * (l1) / noctaves + LFOSCR_VMARG + 4,
                 QString::number((noctaves - l1) * 128 / noctaves));
-    }    
+    }
 
     //Draw function
 
-    octave = 0;
-
     pen.setWidth(notestreak_thick);
     p.setPen(pen);
     for (l1 = 0; l1 < npoints; l1++) {
 
-        octYoffset = 0;
         x = l1 * xscale * nsteps / npoints;
-        ypos = yscale - yscale * p_lfoData.at(l1).value / 128
-                        + LFOSCREEN_VMARGIN;
-        xpos = LFOSCREEN_HMARGIN + x + notestreak_thick / 2;
-        if (p_lfoData.at(l1).muted) {  
+        ypos = yscale - yscale * p_data.at(l1).value / 128
+                        + LFOSCR_VMARG;
+        xpos = LFOSCR_HMARG + x + pen.width() / 2;
+        if (p_data.at(l1).muted) {
             pen.setColor(QColor(100, 40, 5));
-            p.setPen(pen);
         }
         else {
             pen.setColor(QColor(180, 130, 50));
-            p.setPen(pen);
         }
+        p.setPen(pen);
         p.drawLine(xpos, ypos,
-                        xpos + (xscale / beatRes) - notestreak_thick / 2, ypos);
+                        xpos + (xscale / beatRes) - pen.width(), ypos);
     }
-    pen.setWidth(1);
+
+    // Cursor
+    pen.setWidth(notestreak_thick * 2);
+    pen.setColor(QColor(200, 180, 70));
+    p.setPen(pen);
+    x = currentIndex * xscale * (int)nsteps / npoints;
+    xpos = LFOSCR_HMARG + x + pen.width() / 2;
+    p.drawLine(xpos, h - 2,
+                    xpos + (xscale / beatRes) - pen.width(), h - 2);
 }
 
 
-void LfoScreen::updateScreen(const QVector<LfoSample>& lfoData)
+void LfoScreen::updateScreen(const QVector<Sample>& data)
 {
-    p_lfoData = lfoData;
+    p_data = data;
     update();
 }
 
-void LfoScreen::setMuted(bool on)
+void LfoScreen::updateScreen(int p_index)
 {
-    isMuted = on;
+    currentIndex = p_index;
     update();
 }
 
-QSize LfoScreen::sizeHint() const
-{
-    return QSize(LFOSCREEN_MINIMUM_WIDTH, LFOSCREEN_MINIMUM_HEIGHT); 
-}
-
-QSizePolicy LfoScreen::sizePolicy() const
+void LfoScreen::setMuted(bool on)
 {
-    return QSizePolicy(QSizePolicy::MinimumExpanding,
-            QSizePolicy::MinimumExpanding);
+    isMuted = on;
+    update();
 }
 
 void LfoScreen::mouseMoveEvent(QMouseEvent *event)
@@ -196,13 +191,13 @@ void LfoScreen::mouseMoveEvent(QMouseEvent *event)
     mouseX = event->x();
     mouseY = event->y();
     bool cl = false;
-    
-    mouseX = clip(mouseX, LFOSCREEN_HMARGIN, w - LFOSCREEN_HMARGIN, &cl);
-    mouseY = clip(mouseY, LFOSCREEN_VMARGIN + 1, h - LFOSCREEN_VMARGIN, &cl);
-    emit lfoMouseMoved(((double)mouseX - LFOSCREEN_HMARGIN) / 
-                            ((double)w - 2 * LFOSCREEN_HMARGIN + .2), 
-                1. - ((double)mouseY - LFOSCREEN_VMARGIN) / 
-                (h - 2 * LFOSCREEN_VMARGIN), event->buttons());
+
+    mouseX = clip(mouseX, LFOSCR_HMARG, w - LFOSCR_HMARG, &cl);
+    mouseY = clip(mouseY, LFOSCR_VMARG + 1, h - LFOSCR_VMARG, &cl);
+    emit mouseMoved(((double)mouseX - LFOSCR_HMARG) /
+                            ((double)w - 2 * LFOSCR_HMARG + .2),
+                1. - ((double)mouseY - LFOSCR_VMARG) /
+                (h - 2 * LFOSCR_VMARG), event->buttons());
 }
 
 void LfoScreen::mousePressEvent(QMouseEvent *event)
@@ -210,20 +205,20 @@ void LfoScreen::mousePressEvent(QMouseEvent *event)
     mouseX = event->x();
     mouseY = event->y();
     bool cl = false;
-    
-    mouseX = clip(mouseX, LFOSCREEN_HMARGIN, w - LFOSCREEN_HMARGIN, &cl);
-    mouseY = clip(mouseY, LFOSCREEN_VMARGIN + 1, h - LFOSCREEN_VMARGIN, &cl);
-
-    emit lfoMousePressed(((double)mouseX - LFOSCREEN_HMARGIN) / 
-                            ((double)w - 2 * LFOSCREEN_HMARGIN + .2), 
-                1. - ((double)mouseY - LFOSCREEN_VMARGIN) / 
-                (h - 2 * LFOSCREEN_VMARGIN), event->buttons());
+
+    mouseX = clip(mouseX, LFOSCR_HMARG, w - LFOSCR_HMARG, &cl);
+    mouseY = clip(mouseY, LFOSCR_VMARG + 1, h - LFOSCR_VMARG, &cl);
+
+    emit mousePressed(((double)mouseX - LFOSCR_HMARG) /
+                            ((double)w - 2 * LFOSCR_HMARG + .2),
+                1. - ((double)mouseY - LFOSCR_VMARG) /
+                (h - 2 * LFOSCR_VMARG), event->buttons());
 }
 
 void LfoScreen::wheelEvent(QWheelEvent *event)
 {
     mouseW = event->delta();
-    emit lfoWheel(mouseW / 120);
+    emit mouseWheel(mouseW / 120);
     event->accept();
 }
 
@@ -238,6 +233,22 @@ int LfoScreen::clip(int value, int min, int max, bool *outOfRange)
     } else if (tmp < min) {
         tmp = min;
         *outOfRange = true;
-    }  
+    }
     return(tmp);
 }
+
+void LfoScreen::setRecord(bool on)
+{
+    recordMode = on;
+}
+
+QSize LfoScreen::sizeHint() const
+{
+    return QSize(LFOSCR_MIN_W, LFOSCR_MIN_H);
+}
+
+QSizePolicy LfoScreen::sizePolicy() const
+{
+    return QSizePolicy(QSizePolicy::MinimumExpanding,
+            QSizePolicy::MinimumExpanding);
+}
diff --git a/src/lfoscreen.h b/src/lfoscreen.h
index fe7e265..78ce16d 100644
--- a/src/lfoscreen.h
+++ b/src/lfoscreen.h
@@ -1,3 +1,27 @@
+/*!
+ * @file lfoscreen.h
+ * @brief Header for the LfoScreen class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef LFOSCREEN_H
 #define LFOSCREEN_H
 
@@ -6,49 +30,67 @@
 #include <QLabel>
 #include <QMouseEvent>
 #include <QTimer>
+#include <QWheelEvent>
 #include <QSizePolicy>
 #include <QSize>
-#include <QWheelEvent>
+
 #include "midilfo.h"
 
-#define LFOSCREEN_MINIMUM_WIDTH   250
-#define LFOSCREEN_MINIMUM_HEIGHT  120
-#define LFOSCREEN_VMARGIN          10
-#define LFOSCREEN_HMARGIN          20
+#define LFOSCR_MIN_W   250
+#define LFOSCR_MIN_H   120
+#define LFOSCR_VMARG    10
+#define LFOSCR_HMARG    20
 
 
+/*! @brief Drawing widget for visualization of waveforms using QPainter
+ *
+ * LfoScreen is created and embedded by SeqWidget. The painter callback
+ * produces a streak map of a sequence as a piano roll display. The
+ * display is updated by calling LfoScreen::updateScreen() with the
+ * Sample vector as argument. A cursor is placed at the corresponding
+ * vector index by calling LfoScreen::updateScreen() with the integer
+ * current index as an overloaded member.
+ * LfoScreen emits mouse events corresponding to the Qt mousePressed()
+ * and mouseMoved() events. The mouse position is transferred as a
+ * double from 0 ... 1.0 representing the relative mouse position on the
+ * entire LfoScreen display area.
+ */
 class LfoScreen : public QWidget
 {
   Q_OBJECT
 
   private:
     //QTimer *timer;
-    QVector<LfoSample> p_lfoData, lfoData;
+    QVector<Sample> p_data, data;
     int mouseX, mouseY, mouseW;
     int w, h;
+    int currentIndex;
     int clip(int value, int min, int max, bool *outOfRange);
+    bool recordMode;
     bool isMuted;
-    
+
   protected:
     virtual void paintEvent(QPaintEvent *);
-   
+
   public:
     LfoScreen(QWidget* parent=0);
     ~LfoScreen();
     virtual QSize sizeHint() const;
     virtual QSizePolicy sizePolicy() const;
-    
+
   signals:
-    void lfoMousePressed(double, double, int);
-    void lfoMouseMoved(double, double, int);
-    void lfoWheel(int);
-    
-  public slots: 
-    void updateScreen(const QVector<LfoSample>& lfoData);
+    void mousePressed(double, double, int);
+    void mouseMoved(double, double, int);
+    void mouseWheel(int);
+
+  public slots:
+    void updateScreen(const QVector<Sample>& data);
+    void updateScreen(int p_index);
     void mouseMoveEvent(QMouseEvent* event);
     void mousePressEvent(QMouseEvent* event);
+    void setRecord(bool on);
     void wheelEvent(QWheelEvent* event);
     void setMuted(bool on);
 };
-  
+
 #endif
diff --git a/src/lfowidget.cpp b/src/lfowidget.cpp
index 57e6d4d..381982c 100644
--- a/src/lfowidget.cpp
+++ b/src/lfowidget.cpp
@@ -1,18 +1,21 @@
-/*
- *      lfowidget.cpp
- *      
+/*!
+ * @file lfowidget.cpp
+ * @brief Implements the LfoWidget GUI class.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
@@ -39,12 +42,32 @@
 #include "pixmaps/lfowtri.xpm"
 #include "pixmaps/lfowsquare.xpm"
 #include "pixmaps/lfowcustm.xpm"
+#include "pixmaps/seqrecord.xpm"
 #include "config.h"
 
 
 LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QWidget *parent):
     QWidget(parent), midiWorker(p_midiWorker), modified(false)
 {
+    int l1;
+
+    // QSignalMappers allow identifying signal senders for MIDI learn/forget
+    learnSignalMapper = new QSignalMapper(this);
+    connect(learnSignalMapper, SIGNAL(mapped(int)),
+             this, SLOT(midiLearn(int)));
+
+    forgetSignalMapper = new QSignalMapper(this);
+    connect(forgetSignalMapper, SIGNAL(mapped(int)),
+             this, SLOT(midiForget(int)));
+
+    // we need the cancel MIDI Learn action only once for all
+    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
+    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
+    cancelMidiLearnAction->setEnabled(false);
+
+    midiCCNames << "MuteToggle" << "Amplitude" << "Offset" << "WaveForm" << "Frequency"
+                << "RecordToggle" << "unknown";
+
     // Management Buttons on the right top
     QHBoxLayout *manageBoxLayout = new QHBoxLayout;
 
@@ -53,41 +76,72 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
     QToolButton *renameButton = new QToolButton(this);
     renameButton->setDefaultAction(renameAction);
     connect(renameAction, SIGNAL(triggered()), this, SLOT(moduleRename()));
-    
+
     deleteAction = new QAction(QIcon(arpremove_xpm), tr("&Delete..."), this);
     deleteAction->setToolTip(tr("Delete this LFO"));
     QToolButton *deleteButton = new QToolButton(this);
     deleteButton->setDefaultAction(deleteAction);
     connect(deleteAction, SIGNAL(triggered()), this, SLOT(moduleDelete()));
-    
+
     manageBoxLayout->addStretch();
     manageBoxLayout->addWidget(renameButton);
     manageBoxLayout->addWidget(deleteButton);
 
+    // Input group box on right top
+    QGroupBox *inBox = new QGroupBox(tr("Input"), this);
+
+    QLabel *ccnumberInLabel = new QLabel(tr("MIDI &CC#"), inBox);
+    ccnumberInBox = new QSpinBox(inBox);
+    ccnumberInLabel->setBuddy(ccnumberInBox);
+    ccnumberInBox->setRange(0, 127);
+    ccnumberInBox->setKeyboardTracking(false);
+    ccnumberInBox->setValue(74);
+    ccnumberInBox->setToolTip(tr("MIDI Controller number to record"));
+    connect(ccnumberInBox, SIGNAL(valueChanged(int)), this,
+            SLOT(updateCcnumberIn(int)));
+
+
+    QLabel *chInLabel = new QLabel(tr("&Channel"), inBox);
+    chIn = new QComboBox(inBox);
+    for (l1 = 0; l1 < 16; l1++) chIn->addItem(QString::number(l1 + 1));
+    chInLabel->setBuddy(chIn);
+    connect(chIn, SIGNAL(activated(int)), this, SLOT(updateChIn(int)));
+
+    QGridLayout *inBoxLayout = new QGridLayout;
+
+    inBoxLayout->addWidget(ccnumberInLabel, 0, 0);
+    inBoxLayout->addWidget(ccnumberInBox, 0, 1);
+    inBoxLayout->addWidget(chInLabel, 2, 0);
+    inBoxLayout->addWidget(chIn, 2, 1);
+    if (compactStyle) {
+        inBoxLayout->setSpacing(1);
+        inBoxLayout->setMargin(2);
+    }
+
+    inBox->setLayout(inBoxLayout);
+
     // Output group box on right side
     QGroupBox *portBox = new QGroupBox(tr("Output"), this);
 
     QLabel *muteLabel = new QLabel(tr("&Mute"),portBox);
     muteOut = new QCheckBox(this);
 
-    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
-    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
-    cancelMidiLearnAction->setEnabled(false);
-
     muteOut->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+    connect(muteOut, SIGNAL(toggled(bool)), this, SLOT(setMuted(bool)));
+    muteLabel->setBuddy(muteOut);
+
     QAction *muteLearnAction = new QAction(tr("MIDI &Learn"), this);
     muteOut->addAction(muteLearnAction);
-    connect(muteLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnMute()));
+    connect(muteLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(muteLearnAction, 0);
+
     QAction *muteForgetAction = new QAction(tr("MIDI &Forget"), this);
     muteOut->addAction(muteForgetAction);
-    connect(muteForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetMute()));
+    connect(muteForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(muteForgetAction, 0);
 
     muteOut->addAction(cancelMidiLearnAction);
 
-    
-    connect(muteOut, SIGNAL(toggled(bool)), this, SLOT(setMuted(bool)));
-    muteLabel->setBuddy(muteOut);
 
     QLabel *ccnumberLabel = new QLabel(tr("MIDI &CC#"), portBox);
     ccnumberBox = new QSpinBox(portBox);
@@ -100,20 +154,19 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
             SLOT(updateCcnumber(int)));
 
     QLabel *portLabel = new QLabel(tr("&Port"), portBox);
-    portOut = new QSpinBox(portBox);
+    portOut = new QComboBox(portBox);
     portLabel->setBuddy(portOut);
-    portOut->setRange(1, portCount);
-    portOut->setKeyboardTracking(false);
-    connect(portOut, SIGNAL(valueChanged(int)), this, SLOT(updatePortOut(int)));
+    for (l1 = 0; l1 < portCount; l1++) portOut->addItem(QString::number(l1 + 1));
+    connect(portOut, SIGNAL(activated(int)), this, SLOT(updatePortOut(int)));
 
     QLabel *channelLabel = new QLabel(tr("C&hannel"), portBox);
-    channelOut = new QSpinBox(portBox);
+    channelOut = new QComboBox(portBox);
     channelLabel->setBuddy(channelOut);
-    channelOut->setRange(1, 16);
-    channelOut->setKeyboardTracking(false);
-    connect(channelOut, SIGNAL(valueChanged(int)), this,
+    for (l1 = 0; l1 < 16; l1++) channelOut->addItem(QString::number(l1 + 1));
+    connect(channelOut, SIGNAL(activated(int)), this,
             SLOT(updateChannelOut(int)));
 
+
     QGridLayout *portBoxLayout = new QGridLayout;
     portBoxLayout->addWidget(muteLabel, 0, 0);
     portBoxLayout->addWidget(muteOut, 0, 1);
@@ -133,18 +186,24 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
 
     portBox->setLayout(outputLayout);
 
+    QVBoxLayout *inOutBoxLayout = new QVBoxLayout;
+    inOutBoxLayout->addLayout(manageBoxLayout);
+    inOutBoxLayout->addWidget(inBox);
+    inOutBoxLayout->addWidget(portBox);
+    inOutBoxLayout->addStretch();
+
     // group box for wave setup
     QGroupBox *waveBox = new QGroupBox(tr("Wave"), this);
 
-    screen = new LfoScreen(this); 
+    screen = new LfoScreen(this);
     screen->setToolTip(
         tr("Right button to mute points\nLeft button to draw custom wave\nWheel to change offset"));
     screen->setMinimumHeight(80);
-    connect(screen, SIGNAL(lfoMouseMoved(double, double, int)), this,
+    connect(screen, SIGNAL(mouseMoved(double, double, int)), this,
             SLOT(mouseMoved(double, double, int)));
-    connect(screen, SIGNAL(lfoMousePressed(double, double, int)), this,
+    connect(screen, SIGNAL(mousePressed(double, double, int)), this,
             SLOT(mousePressed(double, double, int)));
-    connect(screen, SIGNAL(lfoWheel(int)), this,
+    connect(screen, SIGNAL(mouseWheel(int)), this,
             SLOT(mouseWheel(int)));
     QLabel *waveFormBoxLabel = new QLabel(tr("&Waveform"), waveBox);
     waveFormBox = new QComboBox(waveBox);
@@ -162,22 +221,26 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
     connect(waveFormBox, SIGNAL(activated(int)), this,
             SLOT(updateWaveForm(int)));
     waveFormBox->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+
     QAction *waveFormBoxLearnAction = new QAction(tr("MIDI &Learn"), this);
     waveFormBox->addAction(waveFormBoxLearnAction);
-    connect(waveFormBoxLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnWaveFormBox()));
+    connect(waveFormBoxLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(waveFormBoxLearnAction, 3);
+
     QAction *waveFormBoxForgetAction = new QAction(tr("MIDI &Forget"), this);
     waveFormBox->addAction(waveFormBoxForgetAction);
-    connect(waveFormBoxForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetWaveFormBox()));
-    
+    connect(waveFormBoxForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(waveFormBoxForgetAction, 3);
+
     waveFormBox->addAction(cancelMidiLearnAction);
 
+
     QLabel *freqBoxLabel = new QLabel(tr("&Frequency"),
             waveBox);
     freqBox = new QComboBox(waveBox);
     freqBoxLabel->setBuddy(freqBox);
     QStringList names;
-    names << "1/4" << "1/2" << "3/4" << "1" << "2" << "3" 
+    names << "1/4" << "1/2" << "3/4" << "1" << "2" << "3"
         << "4" << "5" << "6" << "7" << "8";
     freqBox->insertItems(0, names);
     freqBox->setCurrentIndex(3);
@@ -186,16 +249,19 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
     freqBox->setMinimumContentsLength(3);
     connect(freqBox, SIGNAL(activated(int)), this,
             SLOT(updateFreq(int)));
-            
+
     freqBox->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+
     QAction *freqBoxLearnAction = new QAction(tr("MIDI &Learn"), this);
     freqBox->addAction(freqBoxLearnAction);
-    connect(freqBoxLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnFreqBox()));
+    connect(freqBoxLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(freqBoxLearnAction, 4);
+
     QAction *freqBoxForgetAction = new QAction(tr("MIDI &Forget"), this);
     freqBox->addAction(freqBoxForgetAction);
-    connect(freqBoxForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetFreqBox()));
-    
+    connect(freqBoxForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(freqBoxForgetAction, 4);
+
     freqBox->addAction(cancelMidiLearnAction);
 
     QLabel *resBoxLabel = new QLabel(tr("&Resolution"),
@@ -224,40 +290,66 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
     connect(sizeBox, SIGNAL(activated(int)), this,
             SLOT(updateSize(int)));
 
+    QLabel *recordButtonLabel = new QLabel(tr("Re&cord"), waveBox);
+    recordAction = new QAction(QIcon(seqrecord_xpm), tr("Re&cord"), waveBox);
+    recordAction->setToolTip(tr("Record incoming controller"));
+    recordAction->setCheckable(true);
+    QToolButton *recordButton = new QToolButton(waveBox);
+    recordButton->setDefaultAction(recordAction);
+    recordButtonLabel->setBuddy(recordButton);
+    connect(recordAction, SIGNAL(toggled(bool)), this, SLOT(setRecord(bool)));
+    recordButton->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
+
+    QAction *recordLearnAction = new QAction(tr("MIDI &Learn"), this);
+    recordButton->addAction(recordLearnAction);
+    connect(recordLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(recordLearnAction, 5);
+
+    QAction *recordForgetAction = new QAction(tr("MIDI &Forget"), this);
+    recordButton->addAction(recordForgetAction);
+    connect(recordForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(recordForgetAction, 5);
+
+    recordButton->addAction(cancelMidiLearnAction);
+
     amplitude = new Slider(0, 127, 1, 8, 64, Qt::Horizontal,
             tr("&Amplitude"), waveBox);
 
     amplitude->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+    connect(amplitude, SIGNAL(valueChanged(int)), this,
+            SLOT(updateAmp(int)));
+
     QAction *amplitudeLearnAction = new QAction(tr("MIDI &Learn"), this);
     amplitude->addAction(amplitudeLearnAction);
-    connect(amplitudeLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnAmp()));
+    connect(amplitudeLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(amplitudeLearnAction, 1);
+
     QAction *amplitudeForgetAction = new QAction(tr("MIDI &Forget"), this);
     amplitude->addAction(amplitudeForgetAction);
-    connect(amplitudeForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetAmp()));
-    
+    connect(amplitudeForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(amplitudeForgetAction, 1);
+
     amplitude->addAction(cancelMidiLearnAction);
 
-    connect(amplitude, SIGNAL(valueChanged(int)), this,
-            SLOT(updateAmp(int)));
 
     offset = new Slider(0, 127, 1, 8, 0, Qt::Horizontal,
             tr("&Offset"), waveBox);
-
     offset->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+    connect(offset, SIGNAL(valueChanged(int)), this,
+            SLOT(updateOffs(int)));
+
     QAction *offsetLearnAction = new QAction(tr("MIDI &Learn"), this);
     offset->addAction(offsetLearnAction);
-    connect(offsetLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnOffs()));
+    connect(offsetLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(offsetLearnAction, 2);
+
     QAction *offsetForgetAction = new QAction(tr("MIDI &Forget"), this);
     offset->addAction(offsetForgetAction);
-    connect(offsetForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetOffs()));
-    
+    connect(offsetForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(offsetForgetAction, 2);
+
     offset->addAction(cancelMidiLearnAction);
 
-    connect(offset, SIGNAL(valueChanged(int)), this,
-            SLOT(updateOffs(int)));
-    
     QVBoxLayout* sliderLayout = new QVBoxLayout;
     sliderLayout->addWidget(amplitude);
     sliderLayout->addWidget(offset);
@@ -268,21 +360,23 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
     }
 
     QGridLayout *paramBoxLayout = new QGridLayout;
-    paramBoxLayout->addWidget(waveFormBoxLabel, 0, 0);
-    paramBoxLayout->addWidget(waveFormBox, 0, 1);
-    paramBoxLayout->addWidget(freqBoxLabel, 1, 0);
-    paramBoxLayout->addWidget(freqBox, 1, 1);
-    paramBoxLayout->addWidget(resBoxLabel, 0, 2);
-    paramBoxLayout->addWidget(resBox, 0, 3);
-    paramBoxLayout->addWidget(sizeBoxLabel, 1, 2);
-    paramBoxLayout->addWidget(sizeBox, 1, 3);
-    paramBoxLayout->setColumnStretch(4, 4);
-    
+    paramBoxLayout->addWidget(recordButtonLabel, 0, 0);
+    paramBoxLayout->addWidget(recordButton, 0, 1);
+    paramBoxLayout->addWidget(waveFormBoxLabel, 0, 2);
+    paramBoxLayout->addWidget(waveFormBox, 0, 3);
+    paramBoxLayout->addWidget(freqBoxLabel, 1, 2);
+    paramBoxLayout->addWidget(freqBox, 1, 3);
+    paramBoxLayout->addWidget(resBoxLabel, 0, 4);
+    paramBoxLayout->addWidget(resBox, 0, 5);
+    paramBoxLayout->addWidget(sizeBoxLabel, 1, 4);
+    paramBoxLayout->addWidget(sizeBox, 1, 5);
+    paramBoxLayout->setColumnStretch(6, 6);
+
     if (compactStyle) {
         paramBoxLayout->setSpacing(1);
         paramBoxLayout->setMargin(2);
     }
-           
+
     QGridLayout* waveBoxLayout = new QGridLayout;
     waveBoxLayout->addWidget(screen, 0, 0);
     waveBoxLayout->addLayout(paramBoxLayout, 1, 0);
@@ -292,11 +386,6 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
         waveBoxLayout->setMargin(2);
     }
     waveBox->setLayout(waveBoxLayout);
-    
-    QVBoxLayout *inOutBoxLayout = new QVBoxLayout;
-    inOutBoxLayout->addLayout(manageBoxLayout);
-    inOutBoxLayout->addWidget(portBox);
-    inOutBoxLayout->addStretch();
 
     QHBoxLayout *widgetLayout = new QHBoxLayout;
     widgetLayout->addWidget(waveBox, 1);
@@ -304,7 +393,7 @@ LfoWidget::LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QW
 
     setLayout(widgetLayout);
     updateAmp(64);
-    
+
     ccList.clear();
     lastMute = false;
 }
@@ -322,9 +411,16 @@ void LfoWidget::writeData(QXmlStreamWriter& xml)
 {
     QByteArray tempArray;
     int l1;
-    
+
     xml.writeStartElement(name.left(3));
     xml.writeAttribute("name", name.mid(name.indexOf(':') + 1));
+        xml.writeStartElement("input");
+            xml.writeTextElement("channel", QString::number(
+                midiWorker->chIn));
+            xml.writeTextElement("ccnumber", QString::number(
+                midiWorker->ccnumberIn));
+        xml.writeEndElement();
+
         xml.writeStartElement("output");
             xml.writeTextElement("port", QString::number(
                 midiWorker->portOut));
@@ -333,7 +429,7 @@ void LfoWidget::writeData(QXmlStreamWriter& xml)
             xml.writeTextElement("ccnumber", QString::number(
                 midiWorker->ccnumber));
         xml.writeEndElement();
-    
+
         xml.writeStartElement("waveParams");
             xml.writeTextElement("waveform", QString::number(
                 waveFormBox->currentIndex()));
@@ -348,7 +444,7 @@ void LfoWidget::writeData(QXmlStreamWriter& xml)
             xml.writeTextElement("offset", QString::number(
                 midiWorker->offs));
         xml.writeEndElement();
-      
+
         tempArray.clear();
         l1 = 0;
         while (l1 < midiWorker->muteMask.count()) {
@@ -358,7 +454,7 @@ void LfoWidget::writeData(QXmlStreamWriter& xml)
         xml.writeStartElement("muteMask");
             xml.writeTextElement("data", tempArray.toHex());
         xml.writeEndElement();
-        
+
         tempArray.clear();
         l1 = 0;
         while (l1 < midiWorker->muteMask.count()) {
@@ -368,7 +464,7 @@ void LfoWidget::writeData(QXmlStreamWriter& xml)
         xml.writeStartElement("customWave");
             xml.writeTextElement("data", tempArray.toHex());
         xml.writeEndElement();
-           
+
         xml.writeStartElement("midiControllers");
         for (int l1 = 0; l1 < ccList.count(); l1++) {
             xml.writeStartElement("MIDICC");
@@ -384,71 +480,53 @@ void LfoWidget::writeData(QXmlStreamWriter& xml)
             xml.writeEndElement();
         }
         xml.writeEndElement();
-        
+
     xml.writeEndElement();
 }
 
-void LfoWidget::writeDataText(QTextStream& arpText)
-{
-    int l1 = 0;
-    arpText << midiWorker->channelOut << ' ' 
-        << midiWorker->portOut << ' '
-        << midiWorker->ccnumber << '\n';
-    arpText << freqBox->currentIndex() << ' '
-        << resBox->currentIndex() << ' '
-        << sizeBox->currentIndex() << ' '
-        << midiWorker->amp << ' '
-        << midiWorker->offs << '\n';
-    arpText << "MIDICC" << endl;
-    for (int l1 = 0; l1 < ccList.count(); l1++) {
-        arpText << ccList.at(l1).ID << ' '
-                << ccList.at(l1).ccnumber << ' '
-                << ccList.at(l1).channel << ' '
-                << ccList.at(l1).min << ' '
-                << ccList.at(l1).max << endl;
-    }
-    arpText << "EOCC" << endl;
-
-    arpText << waveFormBox->currentIndex() << '\n';
-    // Write Mute Mask
-    while (l1 < midiWorker->muteMask.count()) {
-        arpText << midiWorker->muteMask.at(l1) << ' ';
-        l1++;
-        if (!(l1 % 32)) arpText << "\n";
-    }
-    arpText << "EOM\n"; // End Of Mute
-    // Write Custom Waveform
-    l1 = 0;
-    while (l1 < midiWorker->customWave.count()) {
-        arpText << midiWorker->customWave.at(l1).value << ' ';
-        l1++;
-        if (!(l1 % 16)) arpText << "\n";
-    }
-    arpText << "EOW\n"; // End Of Wave
-    modified = false;
-}
-                                  
 void LfoWidget::readData(QXmlStreamReader& xml)
 {
-    int ctrlID, ccnumber, channel, min, max;
+    int controlID, ccnumber, channel, min, max;
     int tmp;
     int wvtmp = 0;
-    LfoSample lfoSample;
-    
+    Sample sample;
+
     while (!xml.atEnd()) {
         xml.readNext();
         if (xml.isEndElement())
             break;
-            
+
+        if (xml.isStartElement() && (xml.name() == "input")) {
+            while (!xml.atEnd()) {
+                xml.readNext();
+                if (xml.isEndElement())
+                    break;
+                if (xml.name() == "channel") {
+                    tmp = xml.readElementText().toInt();
+                    chIn->setCurrentIndex(tmp);
+                    updateChIn(tmp);
+                }
+                else if (xml.name() == "ccnumber")
+                    ccnumberInBox->setValue(xml.readElementText().toInt());
+                else skipXmlElement(xml);
+            }
+        }
+
         if (xml.isStartElement() && (xml.name() == "output")) {
             while (!xml.atEnd()) {
                 xml.readNext();
                 if (xml.isEndElement())
                     break;
-                if (xml.name() == "channel")
-                    channelOut->setValue(xml.readElementText().toInt() + 1);
-                else if (xml.name() == "port")
-                    portOut->setValue(xml.readElementText().toInt() + 1);
+                if (xml.name() == "channel") {
+                    tmp = xml.readElementText().toInt();
+                    channelOut->setCurrentIndex(tmp);
+                    updateChannelOut(tmp);
+                }
+                else if (xml.name() == "port") {
+                    tmp = xml.readElementText().toInt();
+                    portOut->setCurrentIndex(tmp);
+                    updatePortOut(tmp);
+                }
                 else if (xml.name() == "ccnumber")
                     ccnumberBox->setValue(xml.readElementText().toInt());
                 else skipXmlElement(xml);
@@ -491,7 +569,7 @@ void LfoWidget::readData(QXmlStreamReader& xml)
                     break;
                 if (xml.isStartElement() && (xml.name() == "data")) {
                     midiWorker->muteMask.clear();
-                    QByteArray tmpArray = 
+                    QByteArray tmpArray =
                             QByteArray::fromHex(xml.readElementText().toLatin1());
                     for (int l1 = 0; l1 < tmpArray.count(); l1++) {
                         midiWorker->muteMask.append(tmpArray.at(l1));
@@ -507,15 +585,15 @@ void LfoWidget::readData(QXmlStreamReader& xml)
                     break;
                 if (xml.isStartElement() && (xml.name() == "data")) {
                     midiWorker->customWave.clear();
-                    QByteArray tmpArray = 
+                    QByteArray tmpArray =
                             QByteArray::fromHex(xml.readElementText().toLatin1());
-                    int step = TICKS_PER_QUARTER / midiWorker->res;
+                    int step = TPQN / midiWorker->res;
                     int lt = 0;
                     for (int l1 = 0; l1 < tmpArray.count(); l1++) {
-                        lfoSample.value = tmpArray.at(l1);
-                        lfoSample.tick = lt;
-                        lfoSample.muted = midiWorker->muteMask.at(l1);
-                        midiWorker->customWave.append(lfoSample);
+                        sample.value = tmpArray.at(l1);
+                        sample.tick = lt;
+                        sample.muted = midiWorker->muteMask.at(l1);
+                        midiWorker->customWave.append(sample);
                         lt+=step;
                     }
                 }
@@ -528,7 +606,7 @@ void LfoWidget::readData(QXmlStreamReader& xml)
                 if (xml.isEndElement())
                     break;
                 if (xml.isStartElement() && (xml.name() == "MIDICC")) {
-                    ctrlID = xml.attributes().value("CtrlID").toString().toInt();
+                    controlID = xml.attributes().value("CtrlID").toString().toInt();
                     ccnumber = -1;
                     channel = -1;
                     min = -1;
@@ -548,8 +626,8 @@ void LfoWidget::readData(QXmlStreamReader& xml)
                         else skipXmlElement(xml);
                     }
                     if ((-1 < ccnumber) && (-1 < channel) && (-1 < min) && (-1 < max))
-                        appendMidiCC(ctrlID, ccnumber, channel, min, max);
-                    else qWarning("Controller data incomplete");                 
+                        appendMidiCC(controlID, ccnumber, channel, min, max);
+                    else qWarning("Controller data incomplete");
                 }
                 else skipXmlElement(xml);
             }
@@ -567,43 +645,43 @@ void LfoWidget::skipXmlElement(QXmlStreamReader& xml)
         qWarning("Unknown Element in XML File: %s",qPrintable(xml.name().toString()));
         while (!xml.atEnd()) {
             xml.readNext();
-    
+
             if (xml.isEndElement())
                 break;
-    
+
             if (xml.isStartElement()) {
                 skipXmlElement(xml);
             }
         }
     }
 }
- 
+
 void LfoWidget::readDataText(QTextStream& arpText)
 {
     QString qs, qs2;
     int l1, lt, wvtmp;
-    LfoSample lfoSample;
-    
+    Sample sample;
+
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
-    channelOut->setValue(qs2.toInt() + 1);
-    qs2 = qs.section(' ', 1, 1); 
-    portOut->setValue(qs2.toInt() + 1);
-    qs2 = qs.section(' ', 2, 2); 
+    qs2 = qs.section(' ', 0, 0);
+    channelOut->setCurrentIndex(qs2.toInt());
+    qs2 = qs.section(' ', 1, 1);
+    portOut->setCurrentIndex(qs2.toInt());
+    qs2 = qs.section(' ', 2, 2);
     ccnumberBox->setValue(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
+    qs2 = qs.section(' ', 0, 0);
     freqBox->setCurrentIndex(qs2.toInt());
     updateFreq(qs2.toInt());
     qs2 = qs.section(' ', 1, 1);
     resBox->setCurrentIndex(qs2.toInt());
     updateRes(qs2.toInt());
-    qs2 = qs.section(' ', 2, 2); 
+    qs2 = qs.section(' ', 2, 2);
     sizeBox->setCurrentIndex(qs2.toInt());
     updateSize(qs2.toInt());
-    qs2 = qs.section(' ', 3, 3); 
+    qs2 = qs.section(' ', 3, 3);
     amplitude->setValue(qs2.toInt());
-    qs2 = qs.section(' ', 4, 4); 
+    qs2 = qs.section(' ', 4, 4);
     offset->setValue(qs2.toInt());
     qs = arpText.readLine();
     if (qs == "MIDICC")
@@ -611,7 +689,7 @@ void LfoWidget::readDataText(QTextStream& arpText)
         qs = arpText.readLine();
         while (qs != "EOCC") {
             qs2 = qs.section(' ', 0, 0);
-            int ctrlID = qs2.toInt();
+            int controlID = qs2.toInt();
             qs2 = qs.section(' ', 1, 1);
             int ccnumber = qs2.toInt();
             qs2 = qs.section(' ', 2, 2);
@@ -620,16 +698,16 @@ void LfoWidget::readDataText(QTextStream& arpText)
             int min = qs2.toInt();
             qs2 = qs.section(' ', 4, 4);
             int max = qs2.toInt();
-            appendMidiCC(ctrlID, ccnumber, channel, min, max);
+            appendMidiCC(controlID, ccnumber, channel, min, max);
             qs = arpText.readLine();
         }
     qs = arpText.readLine();
     }
 
     wvtmp = qs.toInt();
-    
+
     // Read Mute Mask
-    int step = TICKS_PER_QUARTER / midiWorker->res;
+    int step = TPQN / midiWorker->res;
     qs = arpText.readLine();
     if (qs.isEmpty() || (qs == "EOP")) return;
     qs2 = qs.section(' ', 0, 0);
@@ -641,7 +719,7 @@ void LfoWidget::readDataText(QTextStream& arpText)
         if (!(l1%32)) qs = arpText.readLine();
         qs2 = qs.section(' ', l1%32, l1%32);
     }
-    
+
     // Read Custom Waveform
     qs = arpText.readLine();
     qs2 = qs.section(' ', 0, 0);
@@ -649,10 +727,10 @@ void LfoWidget::readDataText(QTextStream& arpText)
     l1 = 0;
     lt = 0;
     while (qs2 !="EOW") {
-        lfoSample.value=qs2.toInt();
-        lfoSample.tick = lt;
-        lfoSample.muted = midiWorker->muteMask.at(l1);
-        midiWorker->customWave.append(lfoSample);
+        sample.value=qs2.toInt();
+        sample.tick = lt;
+        sample.muted = midiWorker->muteMask.at(l1);
+        midiWorker->customWave.append(sample);
         lt+=step;
         l1++;
         if (!(l1%16)) qs = arpText.readLine();
@@ -661,47 +739,29 @@ void LfoWidget::readDataText(QTextStream& arpText)
     waveFormBox->setCurrentIndex(wvtmp);
     updateWaveForm(wvtmp);
     modified = false;
-}                                      
+}
 
 void LfoWidget::loadWaveForms()
 {
-    waveForms << tr("Sine") << tr("Saw up") << tr("Triangle") 
+    waveForms << tr("Sine") << tr("Saw up") << tr("Triangle")
         << tr("Saw down") << tr("Square") << tr("Custom");
 }
 
-void LfoWidget::setMuted(bool on)
-{
-    midiWorker->setMuted(on);
-    screen->setMuted(on);
-}
-
-void LfoWidget::setPortOut(int value)
-{
-    portOut->setValue(value);
-    modified = true;
-}
-
-void LfoWidget::setChannelOut(int value)
-{
-    channelOut->setValue(value);
-    modified = true;
-}
-
-void LfoWidget::updatePortOut(int value)
+void LfoWidget::updateCcnumber(int val)
 {
-    midiWorker->portOut = value - 1;
+    midiWorker->ccnumber = val;
     modified = true;
 }
 
-void LfoWidget::updateChannelOut(int value)
+void LfoWidget::updateChIn(int value)
 {
-    midiWorker->channelOut = value - 1;
+    midiWorker->chIn = value - 1;
     modified = true;
 }
 
-void LfoWidget::updateCcnumber(int val)
+void LfoWidget::updateCcnumberIn(int val)
 {
-    midiWorker->ccnumber = val;
+    midiWorker->ccnumberIn = val;
     modified = true;
 }
 
@@ -709,8 +769,8 @@ void LfoWidget::updateWaveForm(int val)
 {
     if (val > 5) return;
     midiWorker->updateWaveForm(val);
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     bool isCustom = (val == 5);
     if (isCustom) newCustomOffset();
     amplitude->setDisabled(isCustom);
@@ -718,49 +778,55 @@ void LfoWidget::updateWaveForm(int val)
     modified = true;
 }
 
+void LfoWidget::updateScreen(int val)
+{
+    if (midiWorker->isRecording) {
+        midiWorker->getData(&data);
+        screen->updateScreen(data);
+    }
+    else {
+        screen->updateScreen(val);
+    }
+}
+
 void LfoWidget::updateFreq(int val)
 {
     if (val > 10) return;
-    midiWorker->freq = lfoFreqValues[val];
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->updateFrequency(lfoFreqValues[val]);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
 void LfoWidget::updateRes(int val)
 {
-    midiWorker->res = lfoResValues[val];
-    midiWorker->resizeAll();
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->updateResolution(lfoResValues[val]);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
 void LfoWidget::updateSize(int val)
 {
-    midiWorker->size = val + 1;
-    midiWorker->resizeAll();
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->updateSize(val + 1);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
 void LfoWidget::updateAmp(int val)
 {
-    midiWorker->amp = val;
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->updateAmplitude(val);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
 void LfoWidget::updateOffs(int val)
 {
-    if (waveFormBox->currentIndex() == 5) {
-        midiWorker->updateCustomWaveOffset(val);
-    }
-    midiWorker->offs = val;
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->updateOffset(val);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
@@ -776,8 +842,8 @@ void LfoWidget::newCustomOffset()
 {
     int min = 127;
     int value;
-    for (int l1 = 0; l1 < lfoData.count() - 1; l1++) {
-        value = lfoData.at(l1).value;
+    for (int l1 = 0; l1 < data.count() - 1; l1++) {
+        value = data.at(l1).value;
         if (value < min) min = value;
     }
     midiWorker->cwmin = min;
@@ -788,7 +854,7 @@ void LfoWidget::mouseMoved(double mouseX, double mouseY, int buttons)
 {
     if (buttons == 2) {
         midiWorker->setMutePoint(mouseX, lastMute);
-    } 
+    }
     else {
         if (waveFormBox->currentIndex() < 5) {
             copyToCustom();
@@ -796,8 +862,8 @@ void LfoWidget::mouseMoved(double mouseX, double mouseY, int buttons)
         midiWorker->setCustomWavePoint(mouseX, mouseY, false);
         newCustomOffset();
     }
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
@@ -805,7 +871,7 @@ void LfoWidget::mousePressed(double mouseX, double mouseY, int buttons)
 {
     if (buttons == 2) {
         lastMute = midiWorker->toggleMutePoint(mouseX);
-    } 
+    }
     else {
         if (waveFormBox->currentIndex() < 5) {
             copyToCustom();
@@ -813,8 +879,8 @@ void LfoWidget::mousePressed(double mouseX, double mouseY, int buttons)
         midiWorker->setCustomWavePoint(mouseX, mouseY, true);
         newCustomOffset();
     }
-    midiWorker->getData(&lfoData);
-    screen->updateScreen(lfoData);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
@@ -826,6 +892,46 @@ void LfoWidget::mouseWheel(int step)
     offset->setValue(cv + step);
 }
 
+void LfoWidget::setMuted(bool on)
+{
+    midiWorker->setMuted(on);
+    screen->setMuted(on);
+}
+
+void LfoWidget::setRecord(bool on)
+{
+    if (!on) {
+        midiWorker->isRecording = false;
+        newCustomOffset();
+    }
+    midiWorker->recordMode = on;
+    screen->setRecord(on);
+}
+
+void LfoWidget::setPortOut(int value)
+{
+    portOut->setCurrentIndex(value);
+    modified = true;
+}
+
+void LfoWidget::setChannelOut(int value)
+{
+    channelOut->setCurrentIndex(value);
+    modified = true;
+}
+
+void LfoWidget::updatePortOut(int value)
+{
+    midiWorker->portOut = value;
+    modified = true;
+}
+
+void LfoWidget::updateChannelOut(int value)
+{
+    midiWorker->channelOut = value;
+    modified = true;
+}
+
 bool LfoWidget::isModified()
 {
     return modified;
@@ -847,51 +953,41 @@ void LfoWidget::moduleDelete()
             == QMessageBox::No) {
         return;
     }
-    emit lfoRemove(ID);
+    emit moduleRemove(ID);
 }
 
 void LfoWidget::moduleRename()
 {
     QString newname, oldname;
     bool ok;
-    
+
     oldname = name;
 
     newname = QInputDialog::getText(this, APP_NAME,
                 tr("New Name"), QLineEdit::Normal, oldname.mid(4), &ok);
-                
+
     if (ok && !newname.isEmpty()) {
         name = "LFO:" + newname;
         emit dockRename(name, parentDockID);
     }
 }
 
-void LfoWidget::appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int max)
+void LfoWidget::appendMidiCC(int controlID, int ccnumber, int channel, int min, int max)
 {
     MidiCC midiCC;
     int l1 = 0;
-    switch (ctrlID) {
-        case 0: midiCC.name = "MuteToggle";
-        break;
-        case 1: midiCC.name = "Amplitude";
-        break;
-        case 2: midiCC.name = "Offset";
-        break;
-        case 3: midiCC.name = "WaveForm";
-        break;
-        default: midiCC.name = "Frequency";
-    }
-    midiCC.ID = ctrlID;
+    midiCC.name = midiCCNames.at(controlID);
+    midiCC.ID = controlID;
     midiCC.ccnumber = ccnumber;
     midiCC.channel = channel;
     midiCC.min = min;
     midiCC.max = max;
-    
-    while ( (l1 < ccList.count()) && 
-        ((ctrlID != ccList.at(l1).ID) || 
+
+    while ( (l1 < ccList.count()) &&
+        ((controlID != ccList.at(l1).ID) ||
         (ccnumber != ccList.at(l1).ccnumber) ||
         (channel != ccList.at(l1).channel)) ) l1++;
-    
+
     if (ccList.count() == l1) {
         ccList.append(midiCC);
         qWarning("MIDI Controller %d appended for %s"
@@ -901,17 +997,17 @@ void LfoWidget::appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int
         qWarning("MIDI Controller %d already attributed to %s"
                 , ccnumber, qPrintable(midiCC.name));
     }
-        
+
     cancelMidiLearnAction->setEnabled(false);
     modified = true;
 }
 
-void LfoWidget::removeMidiCC(int ctrlID, int ccnumber, int channel)
+void LfoWidget::removeMidiCC(int controlID, int ccnumber, int channel)
 {
     for (int l1 = 0; l1 < ccList.count(); l1++) {
-        if (ccList.at(l1).ID == ctrlID) {
+        if (ccList.at(l1).ID == controlID) {
             if (((ccList.at(l1).ccnumber == ccnumber)
-                    && (ccList.at(l1).channel == channel)) 
+                    && (ccList.at(l1).channel == channel))
                     || (0 > channel)) {
                 ccList.remove(l1);
                 l1--;
@@ -922,64 +1018,16 @@ void LfoWidget::removeMidiCC(int ctrlID, int ccnumber, int channel)
     modified = true;
 }
 
-void LfoWidget::midiLearnMute()
-{
-    emit setMidiLearn(parentDockID, ID, 0);
-    qWarning("Requesting Midi Learn for MuteToggle");
-    cancelMidiLearnAction->setEnabled(true);
-}
-
-void LfoWidget::midiForgetMute()
-{
-    removeMidiCC(0, 0, -1);
-}
-
-void LfoWidget::midiLearnOffs()
-{
-    emit setMidiLearn(parentDockID, ID, 2);
-    qWarning("Requesting Midi Learn for Offset");
-    cancelMidiLearnAction->setEnabled(true);
-}
-
-void LfoWidget::midiForgetOffs()
-{
-    removeMidiCC(2, 0, -1);
-}
-
-void LfoWidget::midiLearnAmp()
-{
-    emit setMidiLearn(parentDockID, ID, 1);
-    qWarning("Requesting Midi Learn for WaveForm");
-    cancelMidiLearnAction->setEnabled(true);
-}
-
-void LfoWidget::midiForgetAmp()
-{
-    removeMidiCC(1, 0, -1);
-}
-
-void LfoWidget::midiLearnWaveFormBox()
-{
-    emit setMidiLearn(parentDockID, ID, 3);
-    qWarning("Requesting Midi Learn for WaveForm");
-    cancelMidiLearnAction->setEnabled(true);
-}
-
-void LfoWidget::midiForgetWaveFormBox()
-{
-    removeMidiCC(4, 0, -1);
-}
-
-void LfoWidget::midiLearnFreqBox()
+void LfoWidget::midiLearn(int controlID)
 {
-    emit setMidiLearn(parentDockID, ID, 4);
-    qWarning("Requesting Midi Learn for Frequency");
+    emit setMidiLearn(parentDockID, ID, controlID);
+    qWarning("Requesting Midi Learn for %s", qPrintable(midiCCNames.at(controlID)));
     cancelMidiLearnAction->setEnabled(true);
 }
 
-void LfoWidget::midiForgetFreqBox()
+void LfoWidget::midiForget(int controlID)
 {
-    removeMidiCC(4, 0, -1);
+    removeMidiCC(controlID, 0, -1);
 }
 
 void LfoWidget::midiLearnCancel()
diff --git a/src/lfowidget.h b/src/lfowidget.h
index c51d20e..35e6d7a 100644
--- a/src/lfowidget.h
+++ b/src/lfowidget.h
@@ -1,18 +1,21 @@
-/*
- *      lfowidget.h
- *      
+/*!
+ * @file lfowidget.h
+ * @brief Member definitions for the LfoWidget GUI class.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
@@ -25,6 +28,7 @@
 #include <QAction>
 #include <QComboBox>
 #include <QCheckBox>
+#include <QSignalMapper>
 #include <QSpinBox>
 #include <QString>
 #include <QTextStream>
@@ -36,98 +40,420 @@
 #include "slider.h"
 #include "lfoscreen.h"
 
+/*! This array holds the currently available resolution values.
+ */
 const int lfoResValues[9] = {1, 2, 4, 8, 16, 32, 64, 96, 192};
+/*! This array holds the currently available frequency values.
+ */
 const int lfoFreqValues[11] = {1, 2, 3, 4, 8, 12, 16, 20, 24, 28, 32};
 #ifndef MIDICC_H
+
+/*! @brief Structure holding all elements of a MIDI controller allocated to
+ * a QMidiArp parameter assigned via MIDI learn
+ */
 struct MidiCC {
-        QString name;
-        int min;
-        int max;
-        int ccnumber;
-        int channel;
-        int ID;
-    };    
+        QString name;   /**< @brief Name of the assigned parameter (GUI element)*/
+        int min;        /**< @brief Value output when the CC value is 0 */
+        int max;        /**< @brief Value output when the CC value is 127 */
+        int ccnumber;   /**< @brief MIDI CC number of the assigned controller event */
+        int channel;    /**< @brief MIDI channel on which the controller has to come in */
+        int ID;         /**< @brief Internal ID of the assigned parameter (GUI element)*/
+    };
 #define MIDICC_H
 #endif
 
+/*!
+ * @brief GUI class associated with and controlling a MidiLfo worker
+
+ * It controls the MidiLfo LFO and
+ * is created alongwith each MidiLfo and embedded in a DockWidget on
+ * MainWindow level. It can read its parameter set from an XML stream
+ * by calling its readData member. It manages a LfoWidget::ccList
+ * for each
+ * instance for MIDI controllers attributed through the MIDILearn
+ * context menu. It instantiates an LfoScreen and interacts with it.
+ *
+ *
+*/
 class LfoWidget : public QWidget
 {
     Q_OBJECT
 
+    QComboBox *chIn;
+    QSpinBox  *ccnumberInBox;
+    QSpinBox  *ccnumberBox;
     // Output channel / port (ALSA Sequencer)
-    QSpinBox *channelOut, *portOut, *ccnumberBox;
+    QComboBox *channelOut, *portOut;
     QComboBox *resBox, *sizeBox;
+
     QAction *deleteAction, *renameAction;
     QAction *cancelMidiLearnAction;
- 
+    QSignalMapper *learnSignalMapper, *forgetSignalMapper;
+    QStringList midiCCNames; /**< List of from GUI-element names for index = ControlID */
+
     MidiLfo *midiWorker;
-    QVector<LfoSample> lfoData;
-    bool modified, lastMute;
-    
+    QVector<Sample> data;
+    bool modified;              /**< Is set to True if unsaved parameter modifications exist */
+    bool lastMute;              /**< Contains the mute state of the last waveForm point modified by mouse click*/
+/*!
+* @brief This function allows ignoring one XML element in the XML stream
+* passed by the caller.
+*
+* It also advances the stream read-in. It is used to
+* ignore unknown elements for both-ways-compatibility
+*
+* @param xml reference to QXmlStreamReader containing the open XML stream
+*/
+    void skipXmlElement(QXmlStreamReader& xml);
+/*!
+* @brief This function populates the LfoWidget::waveForms list with
+* waveform names.
+*
+* @par Currently they are
+*   - 0 Sine
+*   - 1 Saw up
+*   - 2 Triangle
+*   - 3 Saw Down
+*   - 4 Square
+*   - 5 Custom
+*
+*/
+    void loadWaveForms();
+
+/*!
+* @brief This function determines the minimum of the current waveform and
+* sets the LfoWidget::offset slider accordingly.
+*
+* It also sets MidiLfo::cwmin. When a new waveform is drawn, its minimum
+* offset from 0 changes and the offset controller has to be adapted in range.
+*
+*/
+    void newCustomOffset();
+
+/* PUBLIC MEMBERS */
   public:
-    QString name;
+/*!
+ * @brief Constructor for LfoWidget. It creates the GUI and an LfoScreen
+ * instance.
+ *
+ * @param p_midiWorker Associated MidiLfo Object
+ * @param portCount Number of available ALSA MIDI output ports
+ * @param compactStyle If set to True, Widget will use reduced spacing and small fonts
+ * @param parent The parent widget of this module, i.e. MainWindow
+ */
+    LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QWidget* parent=0);
+    ~LfoWidget();
+
+    QString name;               /**< The name of this LfoWidget as shown in the DockWidget TitleBar */
+    int ID;                     /**< Corresponds to the Engine::midiLfoList index of the associated MidiLfo */
+    int parentDockID;           /**< The index of the LfoWidget's parent DockWidget in Engine::moduleWindowList */
+    QVector<MidiCC> ccList;     /**< Contains MIDI controller - GUI element bindings */
+
     LfoScreen *screen;
     QStringList waveForms;
     QCheckBox *muteOut;
     Slider *frequency, *amplitude, *offset;
-    int ID, parentDockID;
-    QVector<MidiCC> ccList;
+    QAction *recordAction;
     QComboBox *waveFormBox, *freqBox;
 
-    LfoWidget(MidiLfo *p_midiWorker, int portCount, bool compactStyle, QWidget* parent=0);
-    ~LfoWidget();
+/*!
+* @brief This function returns the MidiLfo instance associated with this GUI
+* Widget.
+* @return MidiLfo instance associated with this GUI
+*/
     MidiLfo *getMidiWorker();
-    
+
+/*!
+* @brief This function reads all parameters of this LFO from an XML stream
+* passed by the caller, i.e. MainWindow.
+*
+* @param xml QXmlStreamWriter to read from
+*/
     void readData(QXmlStreamReader& xml);
+/*!
+* @brief This function reads all LFO parameters of this module from an old
+* QMidiArp .qma text stream.
+*
+* @param arpText QTextStream to read from
+*/
     void readDataText(QTextStream& arpText);
+/*!
+* @brief This function writes all parameters of this LFO to an XML stream
+* passed by the caller, i.e. MainWindow.
+*
+* @param xml QXmlStreamWriter to write to
+*/
     void writeData(QXmlStreamWriter& xml);
-    void writeDataText(QTextStream& arpText);
-    void skipXmlElement(QXmlStreamReader& xml);
+/*!
+* @brief Settor for the LfoWidget::channelOut spinbox setting the output
+* channel of this module.
+* @param value Number of the output channel to send data to
+*
+*/
     void setChannelOut(int value);
+/*!
+* @brief Settor for the LfoWidget::portOut spinbox setting the output
+* port of this module.
+* @param value Number of the output port to send data to
+*
+*/
     void setPortOut(int value);
-    void loadWaveForms();
+/*!
+* @brief Accessor for LfoWidget::modified.
+* @return True if unsaved parameter modifications exist
+*
+*/
     bool isModified();
+/*!
+* @brief This function sets LfoWidget::modified.
+* @param m Set to True if unsaved parameter modifications appear
+*
+*/
     void setModified(bool);
-    void newCustomOffset();
-  
+
+/* SIGNALS */
   signals:
+/*! @brief Currently not in use. */
     void patternChanged();
-    void lfoRemove(int ID);
-    void dockRename(const QString& name, int parentDockID);
+/*! @brief Emitted to MainWindow::removeLfo for module deletion.
+ *  @param ID The internal LfoWidget::ID of the module to remove
+ *  */
+    void moduleRemove(int ID);
+/*! @brief Emitted to MainWindow::renameDock for module rename.
+ *  @param mname New name of the module
+ *  @param parentDockID SeqWidget::parentDockID of the module to rename
+ * */
+    void dockRename(const QString& mname, int parentDockID);
+/*! @brief Emitted to Engine::setMidiLearn to listen for incoming events.
+ *  @param parentDockID SeqWidget::parentDockID of the module to rename
+ *  @param ID SeqWidget::ID of the module receiving the MIDI controller
+ *  @param controlID ID of the GUI element to be assigned to the controller
+ *  */
     void setMidiLearn(int parentDockID, int ID, int controlID);
-    void unsetMidiLearn();
-    void midiForget(int parentDockID, int ID);
-        
+/*! @brief Forwarded context menu action by signalMapper to call MIDI-Learn/Forget functions.
+ *  @param controlID ID of the GUI element requesting the MIDI controller
+ *  */
+    void triggered(int controlID);
+
+/* PUBLIC SLOTS */
   public slots:
-    void updateChannelOut(int value);
-    void setMuted(bool on);
-    void updatePortOut(int value);
+    void updateChIn(int value);
+    void updateCcnumberIn(int value);
+    void updateScreen(int value);
+    void setRecord(bool on);
+
+/*!
+* @brief Slot for the LfoWidget::waveFormBox combobox setting the waveform
+* of this module.
+* @param val Waveform index to choose as present in LfoWidget::loadWaveForms.
+*
+*/
     void updateWaveForm(int);
+/*!
+* @brief Slot for the LfoWidget::resBox combobox. Sets the resolution
+* of the LFO.
+*
+* It sets MidiLfo::res and updates the LfoScreen of this module.
+* @param val Resolution index from lfoResValues to set.
+*
+*/
     void updateRes(int);
+/*!
+* @brief Slot for the LfoWidget::sizeBox combobox. Sets the waveform size
+* of the LFO.
+*
+* It sets MidiLfo::size and updates the LfoScreen of this module.
+* @param val Size (number of bars) of the waveform.
+*
+*/
     void updateSize(int);
+/*!
+* @brief Slot for the LfoWidget::ccnumberBox spinbox setting the output
+* controller CC number of this module.
+* @param val CC number to send data to
+*
+*/
     void updateCcnumber(int val);
+/*!
+* @brief Slot for the LfoWidget::freqBox combobox. Sets the frequency
+* of the LFO.
+*
+* It sets MidiLfo::freq for this and updates the LfoScreen of this module.
+* @param val Frequency index from lfoFreqValues to set.
+*
+*/
     void updateFreq(int val);
+/*!
+* @brief Slot for the LfoWidget::amplitude slider. Sets the amplitude
+* of the waveform for this LFO.
+*
+* It sets MidiLfo::amp and updates the LfoScreen of this module.
+* @param val Amplitude (0 ... 127) of the calculated waveform.
+*
+*/
     void updateAmp(int val);
+/*!
+* @brief Slot for the LfoWidget::offset slider. Sets the offset
+* of the waveform for this LFO.
+*
+* This function updates the LfoScreen of this module.
+* @param val Offset (0 ... 127) of the waveform.
+*
+*/
     void updateOffs(int val);
+
+/*!
+* @brief Slot for the LfoScreen::mouseMoved signal. This function
+* mutes or sets a wave point when the mouse is moved with held buttons.
+*
+* The mouse events are generated by the LfoScreen.
+* It sets a MidiLfo::setCustomWavePoint and calls LfoWidget::newCustomOffset
+* if the left button is held while moving. It sets a MidiLfo::setMutePoint
+* if the right button is held while moving.
+*
+* @param mouseX Normalized mouse position on LfoScreen in X
+* direction (0.0 ... 1.0)
+* @param mouseY Normalized mouse position on LfoScreen in Y
+* direction (0.0 ... 1.0)
+* @param buttons 1 for left mouse button, 2 for right mouse button
+*
+*/
     void mouseMoved(double, double, int);
+/*!
+* @brief Slot for the LfoScreen::mousePressed signal. This function
+* mutes or sets a wave point when a mouse button is pressed before moving.
+*
+* The mouse events are generated by the LfoScreen.
+* It sets a MidiLfo::setCustomWavePoint and calls LfoWidget::newCustomOffset
+* if the left button is pressed. It toggles a MidiLfo::setMutePoint
+* if the right button is pressed before moving.
+*
+* @param mouseX Normalized mouse position on LfoScreen in X
+* direction (0.0 ... 1.0)
+* @param mouseY Normalized mouse position on LfoScreen in Y
+* direction (0.0 ... 1.0)
+* @param buttons 1 for left mouse button, 2 for right mouse button
+*
+*/
     void mousePressed(double, double, int);
+/*!
+* @brief Slot for the LfoScreen::mouseWheel signal. This function changes the
+* offset by the steps the mouse wheel was turned.
+*
+* The mouse events are generated by the LfoScreen.
+* @param step number of steps and direction the offset will be changed by
+*
+*/
     void mouseWheel(int);
+/*!
+* @brief Slot currently not in use.
+*
+* This function calls the midi worker
+* MidiLfo::copyToCustom, which copies the current waveform to the custom
+* waveform buffer
+*
+* It switches the waveForm combobox to index 5 and calls
+* LfoWidget::updateWaveForm.
+*
+*/
     void copyToCustom();
+
+/*!
+* @brief Slot for the LfoWidget::channelOut spinbox setting the output
+* channel of this module.
+* @param value Number of the output channel to send data to
+*
+*/
+    void updateChannelOut(int value);
+/*!
+* @brief Slot for the LfoWidget::portOut spinbox setting the output
+* port of this module.
+* @param value Number of the output port to send data to
+*
+*/
+    void updatePortOut(int value);
+/*!
+* @brief Slot for the LfoWidget::muteOut checkbox.
+* This function suppresses output of LFO data.
+*
+* It calls
+* MidiLfo::setMuted and LfoScreen::setMuted
+* @param on Set to True for muting this module
+*
+*/
+    void setMuted(bool on);
+/*!
+* @brief Slot for LfoWidget::deleteAction.
+*
+* This function displays a warning and then emits the
+* LfoWidget::moduleRemove signal to MainWindow with the module ID as
+* parameter.
+*/
     void moduleDelete();
+/*!
+* @brief Slot for LfoWidget::renameAction.
+*
+* This function queries a new name then emits the LfoWidget::dockRename
+* signal to MainWindow with the new name and the dockWidget ID to rename.
+*/
     void moduleRename();
-    void appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int max);
-    void removeMidiCC(int ctrlID, int ccnumber, int channel);
-    void midiLearnMute();
-    void midiForgetMute();
-    void midiLearnOffs();
-    void midiForgetOffs();
-    void midiLearnWaveFormBox();
-    void midiForgetWaveFormBox();
-    void midiLearnFreqBox();
-    void midiForgetFreqBox();
-    void midiLearnAmp();
-    void midiForgetAmp();
+/*!
+* @brief This function appends a new MIDI controller - GUI element
+* binding to LfoWidget::ccList.
+*
+* Before appending, it checks whether this binding already exists.
+* @param controlID The ID of the control GUI element (found in
+* LfoWidget::midiCCNames)
+* @param ccnumber The CC of the MIDI controller to be attributed
+* @param channel The MIDI Channel of the MIDI controller to be attributed
+* @param min The minimum value to which the controller range is mapped
+* @param max The maximum value to which the controller range is mapped
+*/
+    void appendMidiCC(int controlID, int ccnumber, int channel, int min, int max);
+/*!
+* @brief This function removes a MIDI controller - GUI element
+* binding from the LfoWidget::ccList.
+*
+* @param controlID The ID of the control GUI element (found in
+* LfoWidget::midiCCNames)
+* @param ccnumber The CC of the MIDI controller to be removed
+* @param channel The MIDI Channel of the MIDI controller to be removed
+*/
+    void removeMidiCC(int controlID, int ccnumber, int channel);
+/*!
+* @brief Slot for LfoWidget::triggered signal created by MIDI-Learn context
+* menu MIDI Learn action.
+*
+* This function sets Engine into
+* MIDI Learn status for this module and controlID.
+* It emits LfoWidget::setMidiLearn with the necessary module and GUI element
+* information parameters.
+* Engine will then wait for an incoming controller event and trigger the
+* attribution by calling appendMidiCC.
+*
+* @param controlID The ID of the control GUI element (found in
+* LfoWidget::midiCCNames)
+*/
+    void midiLearn(int controlID);
+/*!
+* @brief Slot for LfoWidget::triggered signal created by a MIDI-Learn
+* context menu Forget action.
+*
+* This function removes a controller
+* binding attribution by calling removeMidiCC.
+*
+* @param controlID The ID of the control GUI element (found in
+* LfoWidget::midiCCNames)
+*/
+    void midiForget(int controlID);
+/*!
+* @brief Slot for LfoWidget::cancelMidiLearnAction in MIDI-Learn context
+* menu. This function signals cancellation of the MIDI-Learn Process to
+* Engine.
+*
+* It emits LfoWidget::setMidiLearn with controlID set to -1 meaning cancel.
+*/
     void midiLearnCancel();
 };
-  
+
 #endif
diff --git a/src/logwidget.cpp b/src/logwidget.cpp
index f68c5bf..f379a2b 100644
--- a/src/logwidget.cpp
+++ b/src/logwidget.cpp
@@ -1,6 +1,30 @@
+/**
+ * @file logwidget.cpp
+ * @brief Implements the LogWidget QWidget class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
 #include <QString>
 #include <QLabel>
-#include <QSlider> 
+#include <QSlider>
 #include <QPushButton>
 #include <QComboBox>
 #include <QSpinBox>
@@ -8,12 +32,10 @@
 #include <QBoxLayout>
 #include <QGroupBox>
 #include <QDateTime>
-#include <alsa/asoundlib.h>
 
-#include "arpdata.h"
+#include "midievent.h"
 #include "logwidget.h"
 
-
 LogWidget::LogWidget(QWidget *parent) : QWidget(parent)
 {
     logActive = false;
@@ -28,13 +50,13 @@ LogWidget::LogWidget(QWidget *parent) : QWidget(parent)
     QObject::connect(enableLog, SIGNAL(toggled(bool)), this,
             SLOT(enableLogToggle(bool)));
     enableLog->setChecked(logActive);
-    
+
     logMidiClock = new QCheckBox(this);
     logMidiClock->setText(tr("Log &MIDI Clock"));
     QObject::connect(logMidiClock, SIGNAL(toggled(bool)), this,
             SLOT(logMidiToggle(bool)));
     logMidiClock->setChecked(logMidiActive);
-    
+
     QPushButton *clearButton = new QPushButton(tr("&Clear"), this);
     QObject::connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
     QHBoxLayout *buttonBoxLayout = new QHBoxLayout;
@@ -42,7 +64,7 @@ LogWidget::LogWidget(QWidget *parent) : QWidget(parent)
     buttonBoxLayout->addWidget(logMidiClock);
     buttonBoxLayout->addStretch(10);
     buttonBoxLayout->addWidget(clearButton);
-    
+
     QVBoxLayout *logBoxLayout = new QVBoxLayout;
     logBoxLayout->addWidget(logText);
     logBoxLayout->addLayout(buttonBoxLayout);
@@ -53,53 +75,53 @@ LogWidget::~LogWidget()
 {
 }
 
-void LogWidget::appendEvent(snd_seq_event_t *ev) {
+void LogWidget::appendEvent(int type, int data, int channel, int value) {
 
     QString qs, qs2;
 
     if (!logActive) {
         return;
     }
-    switch (ev->type) {
-        case SND_SEQ_EVENT_NOTEON:
+    switch (type) {
+        case EV_NOTEON:
             qs.sprintf("Ch %2d, Note On %3d, Vel %3d",
-                    ev->data.control.channel + 1, 
-                    ev->data.note.note, ev->data.note.velocity);
+                    channel + 1,
+                    data, value);
             break;
-        case SND_SEQ_EVENT_NOTEOFF:
-            qs.sprintf("Ch %2d, Note Off %3d", ev->data.control.channel+1, 
-                    ev->data.note.note);
+        case EV_NOTEOFF:
+            qs.sprintf("Ch %2d, Note Off %3d", channel+1,
+                    data);
             break;
-        case SND_SEQ_EVENT_CONTROLLER:
+        case EV_CONTROLLER:
             logText->setTextColor(QColor(100,160,0));
-            qs.sprintf("Ch %2d, Ctrl %3d, Val %3d", ev->data.control.channel+1, 
-                    ev->data.control.param, ev->data.control.value);
+            qs.sprintf("Ch %2d, Ctrl %3d, Val %3d", channel+1,
+                    data, value);
             break;
-        case SND_SEQ_EVENT_PITCHBEND:
+        case EV_PITCHBEND:
             logText->setTextColor(QColor(100,0,255));
-            qs.sprintf("Ch %2d, Pitch %5d", ev->data.control.channel+1, 
-                    ev->data.control.value);
+            qs.sprintf("Ch %2d, Pitch %5d", channel+1,
+                    value);
             break;
-        case SND_SEQ_EVENT_PGMCHANGE:
+        case EV_PGMCHANGE:
             logText->setTextColor(QColor(0,100,100));
-            qs.sprintf("Ch %2d, PrgChg %5d", ev->data.control.channel+1, 
-                    ev->data.control.value);
+            qs.sprintf("Ch %2d, PrgChg %5d", channel+1,
+                    value);
             break;
-        case SND_SEQ_EVENT_CLOCK:
+        case EV_CLOCK:
             if (logMidiActive) {
                 logText->setTextColor(QColor(150,150,150));
                 qs = tr("MIDI Clock");
             }
             break;
-        case SND_SEQ_EVENT_START:
+        case EV_START:
             logText->setTextColor(QColor(0,192,0));
             qs = tr("MIDI Start (Transport)");
             break;
-        case SND_SEQ_EVENT_CONTINUE:
+        case EV_CONTINUE:
             logText->setTextColor(QColor(0,128,0));
             qs = tr("MIDI Continue (Transport)");
             break;
-        case SND_SEQ_EVENT_STOP:
+        case EV_STOP:
             logText->setTextColor(QColor(128,96,0));
             qs = tr("MIDI Stop (Transport)");
             break;
@@ -108,7 +130,7 @@ void LogWidget::appendEvent(snd_seq_event_t *ev) {
             qs = tr("Unknown event type");
             break;
     }
-    if ((ev->type != SND_SEQ_EVENT_CLOCK) || logMidiActive)
+    if ((type != EV_CLOCK) || logMidiActive)
         logText->append(QTime::currentTime().toString(
                     "hh:mm:ss.zzz") + "  " + qs);
     logText->setTextColor(QColor(0, 0, 255));
diff --git a/src/logwidget.h b/src/logwidget.h
index 97fa0d9..2b26376 100644
--- a/src/logwidget.h
+++ b/src/logwidget.h
@@ -1,3 +1,27 @@
+/**
+ * @file logwidget.h
+ * @brief Member definitions for the LogWidget QWidget class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
 #ifndef LOGWIDGET_H
 #define LOGWIDGET_H
 
@@ -11,11 +35,16 @@
 #include <QTextEdit>
 #include <QDateTime>
 
-#include <alsa/asoundlib.h>
-
-#include "arpdata.h"
-
-
+/*!
+ * @brief Creates a QWidget with three sliders controlling the arpeggiator groove.
+ *
+ * The LogWidget is instantiated by MainWindow on program start. It is
+ * embedded in a DockWindow and shown/hidden by a MainWindow menu entry and
+ * tool button.
+ * The Widget holds a QTextEdit panel, which is filled with information on
+ * each MIDI Event passed by signalling to the LogWidget::appendEvent() slot.
+ * The events are currently emitted by the SeqDriver backend.
+ */
 class LogWidget : public QWidget
 
 {
@@ -33,13 +62,13 @@ class LogWidget : public QWidget
     ~LogWidget();
     QCheckBox *enableLog;
     QCheckBox *logMidiClock;
-     
+
   public slots:
     void logMidiToggle(bool on);
     void enableLogToggle(bool on);
-    void appendEvent(snd_seq_event_t *ev);
+    void appendEvent(int type, int data, int channel, int value);
     void appendText(const QString&);
     void clear();
 };
-  
+
 #endif
diff --git a/src/main.cpp b/src/main.cpp
index bcdcf13..9b95c58 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,34 @@
-#include <getopt.h>  
+/**  @file main.cpp
+ *   @brief Main program file. Instantiates the Application MainWindow.
+ *
+ *      Handles commandline arguments and options before MainWindow
+ *      construction.
+ *   @mainpage A MIDI Arpeggiator, LFO and Step Sequencer for ALSA
+ *   @section Description
+ *      This attempts to give an overview of the architecture of this
+ *      software.
+ *
+ *   @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
+#include <getopt.h>
 #include <QApplication>
 #include <QFileInfo>
 #include <QString>
@@ -18,10 +48,10 @@ static struct option options[] = {
     {0, 0, 0, 0}
 };
 
-int main(int argc, char *argv[])  
+int main(int argc, char *argv[])
 {
     int getopt_return;
-    int option_index; 
+    int option_index;
     int portCount = 2;
     QTextStream out(stdout);
 
@@ -48,7 +78,7 @@ int main(int argc, char *argv[])
 
             case 'p':
                 portCount = atoi(optarg);
-                if (portCount > MAX_PORTS) 
+                if (portCount > MAX_PORTS)
                     portCount = MAX_PORTS;
                 else if (portCount < 1)
                     portCount = 2;
@@ -66,7 +96,7 @@ int main(int argc, char *argv[])
                 QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
         app.installTranslator(&qtTr);
 
-    // translator for qmidiarp messages       
+    // translator for qmidiarp messages
     QTranslator qmidiarpTr;
 
     if (qmidiarpTr.load(QString(PACKAGE "_") + loc.name(), TRANSLATIONSDIR))
diff --git a/src/main.h b/src/main.h
index c69e5a4..8a4db0d 100644
--- a/src/main.h
+++ b/src/main.h
@@ -1,13 +1,42 @@
+/**  @file main.h
+ *   @brief Main include file. Defines global Macros.
+ *
+ *   @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef __MAIN_H
 #define __MAIN_H
 
-#define MAX_PORTS 64
-#define SEQPOOL 2048
-#define LFO_FRAMESIZE 256
-#define MAXNOTES 128
-#define TICKS_PER_QUARTER 192
-#define MIDICLK_TPQ 24
-#define MAXCHORD  33
+#include "midievent.h"
+
+
+#define CT_FOOTSW       0x40
+
+#define MAX_PORTS         64
+#define SEQPOOL         2048
+#define LFO_FRAMELIMIT    16
+#define MAXNOTES         128
+#define TPQN             192
+#define MIDICLK_TPQN      24
+#define MAXCHORD          33
+
 #define QMARCNAME ".qmidiarprc"
 #define COMPACT_STYLE "QLabel { font: 7pt; } \
     QComboBox { font: 7pt; max-height: 15px;} \
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 1674da6..4039113 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1,3 +1,27 @@
+/*!
+ * @file mainwindow.cpp
+ * @brief Implements the MainWindow top-level UI class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #include <QBoxLayout>
 #include <QDir>
 #include <QFile>
@@ -49,11 +73,10 @@ MainWindow::MainWindow(int p_portCount)
     filename = "";
     lastDir = QDir::homePath();
 
-    arpData = new ArpData(this);
-    arpData->registerPorts(p_portCount);
-                
-    midiCCTable = new MidiCCTable(arpData, this);
-    
+    engine = new Engine(p_portCount, this);
+
+    midiCCTable = new MidiCCTable(engine, this);
+
     logWidget = new LogWidget(this);
     logWindow = new QDockWidget(tr("Event Log"), this);
     logWindow->setFeatures(QDockWidget::DockWidgetClosable
@@ -62,13 +85,13 @@ MainWindow::MainWindow(int p_portCount)
     logWindow->setWidget(logWidget);
     logWindow->setObjectName("logWidget");
     addDockWidget(Qt::BottomDockWidgetArea, logWindow);
-    connect(arpData->seqDriver, SIGNAL(midiEvent(snd_seq_event_t *)), 
-            logWidget, SLOT(appendEvent(snd_seq_event_t *)));
+    connect(engine->seqDriver, SIGNAL(midiEvent(int, int, int, int)),
+            logWidget, SLOT(appendEvent(int, int, int, int)));
 
-    passWidget = new PassWidget(arpData, p_portCount, this);
-   
-    connect(this, SIGNAL(runQueue(bool)), 
-            arpData->seqDriver, SLOT(runQueue(bool)));                                
+    passWidget = new PassWidget(engine, p_portCount, this);
+
+    connect(this, SIGNAL(runQueue(bool)),
+            engine->seqDriver, SLOT(setQueueStatus(bool)));
     grooveWidget = new GrooveWidget(this);
     grooveWindow = new QDockWidget(tr("Groove"), this);
     grooveWindow->setFeatures(QDockWidget::DockWidgetClosable
@@ -78,12 +101,12 @@ MainWindow::MainWindow(int p_portCount)
     grooveWindow->setObjectName("grooveWidget");
     grooveWindow->setVisible(true);
     addDockWidget(Qt::BottomDockWidgetArea, grooveWindow);
-    connect(grooveWidget, SIGNAL(newGrooveTick(int)), 
-            arpData->seqDriver, SLOT(setGrooveTick(int)));
-    connect(grooveWidget, SIGNAL(newGrooveVelocity(int)), 
-            arpData->seqDriver, SLOT(setGrooveVelocity(int)));
-    connect(grooveWidget, SIGNAL(newGrooveLength(int)), 
-            arpData->seqDriver, SLOT(setGrooveLength(int)));
+    connect(grooveWidget, SIGNAL(newGrooveTick(int)),
+            engine, SLOT(setGrooveTick(int)));
+    connect(grooveWidget, SIGNAL(newGrooveVelocity(int)),
+            engine, SLOT(setGrooveVelocity(int)));
+    connect(grooveWidget, SIGNAL(newGrooveLength(int)),
+            engine, SLOT(setGrooveLength(int)));
 
     addArpAction = new QAction(QIcon(arpadd_xpm), tr("&New Arp..."), this);
     addArpAction->setShortcut(QKeySequence(tr("Ctrl+A", "Module|New Arp")));
@@ -102,17 +125,17 @@ MainWindow::MainWindow(int p_portCount)
 
 
     fileNewAction = new QAction(QIcon(filenew_xpm), tr("&New"), this);
-    fileNewAction->setShortcut(QKeySequence(QKeySequence::New));    
+    fileNewAction->setShortcut(QKeySequence(QKeySequence::New));
     fileNewAction->setToolTip(tr("Create new arpeggiator file"));
     connect(fileNewAction, SIGNAL(triggered()), this, SLOT(fileNew()));
 
     fileOpenAction = new QAction(QIcon(fileopen_xpm), tr("&Open..."), this);
-    fileOpenAction->setShortcut(QKeySequence(QKeySequence::Open));    
+    fileOpenAction->setShortcut(QKeySequence(QKeySequence::Open));
     fileOpenAction->setToolTip(tr("Open arpeggiator file"));
     connect(fileOpenAction, SIGNAL(triggered()), this, SLOT(fileOpen()));
 
     fileSaveAction = new QAction(QIcon(filesave_xpm), tr("&Save"), this);
-    fileSaveAction->setShortcut(QKeySequence(QKeySequence::Save));    
+    fileSaveAction->setShortcut(QKeySequence(QKeySequence::Save));
     fileSaveAction->setToolTip(tr("Save current arpeggiator file"));
     connect(fileSaveAction, SIGNAL(triggered()), this, SLOT(fileSave()));
 
@@ -123,7 +146,7 @@ MainWindow::MainWindow(int p_portCount)
     connect(fileSaveAsAction, SIGNAL(triggered()), this, SLOT(fileSaveAs()));
 
     fileQuitAction = new QAction(QIcon(filequit_xpm), tr("&Quit"), this);
-    fileQuitAction->setShortcut(QKeySequence(tr("Ctrl+Q", "File|Quit")));    
+    fileQuitAction->setShortcut(QKeySequence(tr("Ctrl+Q", "File|Quit")));
     fileQuitAction->setToolTip(tr("Quit application"));
     connect(fileQuitAction, SIGNAL(triggered()), this, SLOT(close()));
 
@@ -141,7 +164,7 @@ MainWindow::MainWindow(int p_portCount)
     connect(tempoSpin, SIGNAL(valueChanged(int)), this,
             SLOT(updateTempo(int)));
 
-    midiClockAction = new QAction(QIcon(midiclock_xpm), 
+    midiClockAction = new QAction(QIcon(midiclock_xpm),
             tr("&Use incoming MIDI Clock"), this);
     midiClockAction->setCheckable(true);
     midiClockAction->setChecked(false);
@@ -150,17 +173,17 @@ MainWindow::MainWindow(int p_portCount)
             SLOT(midiClockToggle(bool)));
 
 
-    jackSyncAction = new QAction(QIcon(jacktr_xpm), 
+    jackSyncAction = new QAction(QIcon(jacktr_xpm),
             tr("&Connect to Jack Transport"), this);
     jackSyncAction->setCheckable(true);
     jackSyncAction->setChecked(false);
     jackSyncAction->setDisabled(true);
     connect(jackSyncAction, SIGNAL(toggled(bool)), this,
             SLOT(jackSyncToggle(bool)));
-    connect(arpData->seqDriver, SIGNAL(jackShutdown(bool)), 
+    connect(engine->seqDriver, SIGNAL(jackShutdown(bool)),
             jackSyncAction, SLOT(setChecked(bool)));
 
-    
+
     updateRunQueue(false);
 
     QAction* viewLogAction = logWindow->toggleViewAction();
@@ -173,14 +196,14 @@ MainWindow::MainWindow(int p_portCount)
     viewGrooveAction->setText(tr("&Groove Settings"));
     viewGrooveAction->setShortcut(QKeySequence(tr("Ctrl+G", "View|Groove")));
 
-    QAction* viewSettingsAction = new QAction(tr("&Settings"), this); 
+    QAction* viewSettingsAction = new QAction(tr("&Settings"), this);
     viewSettingsAction->setIcon(QIcon(settings_xpm));
     viewSettingsAction->setShortcut(QKeySequence(tr("Ctrl+P",
                     "View|Settings")));
     connect(viewSettingsAction, SIGNAL(triggered()), passWidget, SLOT(show()));
 
-    QMenuBar *menuBar = new QMenuBar; 
-    QMenu *fileMenu = new QMenu(tr("&File"), this); 
+    QMenuBar *menuBar = new QMenuBar;
+    QMenu *fileMenu = new QMenu(tr("&File"), this);
     QMenu *viewMenu = new QMenu(tr("&View"), this);
     QMenu *arpMenu = new QMenu(tr("Mod&ule"), this);
     QMenu *helpMenu = new QMenu(tr("&Help"), this);
@@ -189,11 +212,11 @@ MainWindow::MainWindow(int p_portCount)
     fileMenu->addAction(fileOpenAction);
 
     fileRecentlyOpenedFiles = fileMenu->addMenu(tr("&Recently opened files"));
-    
+
     fileMenu->addAction(fileSaveAction);
     fileMenu->addAction(fileSaveAsAction);
     fileMenu->addSeparator();
-    fileMenu->addAction(fileQuitAction);    
+    fileMenu->addAction(fileQuitAction);
     connect(fileMenu, SIGNAL(aboutToShow()), this,
         SLOT(setupRecentFilesMenu()));
     connect(fileRecentlyOpenedFiles, SIGNAL(triggered(QAction*)), this,
@@ -211,14 +234,14 @@ MainWindow::MainWindow(int p_portCount)
     arpMenu->addSeparator();
 
     helpMenu->addAction(tr("&About %1...").arg(APP_NAME), this,
-            SLOT(helpAbout())); 
+            SLOT(helpAbout()));
     helpMenu->addAction(tr("&About Qt..."), this,
             SLOT(helpAboutQt()));
 
     fileToolBar = new QToolBar(tr("&File Toolbar"), this);
-    fileToolBar->addAction(fileNewAction);    
-    fileToolBar->addAction(fileOpenAction);    
-    fileToolBar->addAction(fileSaveAction);    
+    fileToolBar->addAction(fileNewAction);
+    fileToolBar->addAction(fileOpenAction);
+    fileToolBar->addAction(fileSaveAction);
     fileToolBar->addAction(fileSaveAsAction);
     fileToolBar->setObjectName("fileToolBar");
     fileToolBar->setMaximumHeight(30);
@@ -251,14 +274,13 @@ MainWindow::MainWindow(int p_portCount)
     QWidget *centWidget = new QWidget(this);
     setCentralWidget(centWidget);
     updateWindowTitle();
-    seqEventLocked = false;
 
     if (checkRcFile())
         readRcFile();
-    
+
     if (!installSignalHandlers())
         qWarning("%s", "Signal handlers not installed!");
-    
+
     show();
 }
 
@@ -271,12 +293,12 @@ void MainWindow::updateWindowTitle()
     if (filename.isEmpty())
         setWindowTitle(QString("%1 (%2)")
                 .arg(APP_NAME)
-                .arg(arpData->getAlsaClientId()));
+                .arg(engine->getAlsaClientId()));
     else
         setWindowTitle(QString("%1 - %2  (%3)")
                 .arg(filename)
                 .arg(APP_NAME)
-                .arg(arpData->getAlsaClientId()));
+                .arg(engine->getAlsaClientId()));
 }
 
 void MainWindow::helpAbout()
@@ -296,7 +318,7 @@ void MainWindow::arpNew()
 
     name = QInputDialog::getText(this, APP_NAME,
             tr("Add MIDI Arpeggiator"), QLineEdit::Normal,
-            tr("%1").arg(arpData->midiArpCount() + 1), &ok);
+            tr("%1").arg(engine->midiArpCount() + 1), &ok);
     if (ok && !name.isEmpty()) {
         addArp("Arp:"+name);
     }
@@ -309,7 +331,7 @@ void MainWindow::lfoNew()
 
     name = QInputDialog::getText(this, APP_NAME,
             tr("Add MIDI LFO"), QLineEdit::Normal,
-            tr("%1").arg(arpData->midiLfoCount() + 1), &ok);
+            tr("%1").arg(engine->midiLfoCount() + 1), &ok);
     if (ok && !name.isEmpty()) {
         addLfo("LFO:"+name);
     }
@@ -322,7 +344,7 @@ void MainWindow::seqNew()
 
     name = QInputDialog::getText(this, APP_NAME,
             tr("Add Step Sequencer"), QLineEdit::Normal,
-            tr("%1").arg(arpData->midiSeqCount() + 1), &ok);
+            tr("%1").arg(engine->midiSeqCount() + 1), &ok);
     if (ok && !name.isEmpty()) {
         addSeq("Seq:"+name);
     }
@@ -331,51 +353,51 @@ void MainWindow::seqNew()
 void MainWindow::addArp(const QString& name)
 {
     int count, widgetID;
-    MidiArp *midiArp = new MidiArp();
-    arpData->addMidiArp(midiArp);   
-    ArpWidget *arpWidget = new ArpWidget(midiArp,
-            arpData->getPortCount(), passWidget->compactStyle, this);
+    MidiArp *midiWorker = new MidiArp();
+    engine->addMidiArp(midiWorker);
+    ArpWidget *moduleWidget = new ArpWidget(midiWorker,
+            engine->getPortCount(), passWidget->compactStyle, this);
     // passing compactStyle property was necessary because stylesheet
     // seems to have no effect on layout spacing/margin
-    connect(midiArp, SIGNAL(nextStep(int)),
-            arpWidget->screen, SLOT(updateScreen(int)));
-    connect(arpWidget, SIGNAL(presetsChanged(const QString&, const
-                    QString&, int)), 
+    connect(midiWorker, SIGNAL(nextStep(int)),
+            moduleWidget->screen, SLOT(updateScreen(int)));
+    connect(moduleWidget, SIGNAL(presetsChanged(const QString&, const
+                    QString&, int)),
             this, SLOT(updatePatternPresets(const QString&, const
                     QString&, int)));
-    connect(arpWidget, SIGNAL(arpRemove(int)), 
+    connect(moduleWidget, SIGNAL(moduleRemove(int)),
             this, SLOT(removeArp(int)));
-    connect(arpWidget, SIGNAL(dockRename(const QString&, int)), 
+    connect(moduleWidget, SIGNAL(dockRename(const QString&, int)),
             this, SLOT(renameDock(const QString&, int)));
-    connect(arpWidget, SIGNAL(setMidiLearn(int, int, int)), 
-            arpData, SLOT(setMidiLearn(int, int, int)));
-
-    connect(grooveWidget, SIGNAL(newGrooveTick(int)), 
-            arpWidget->screen, SLOT(setGrooveTick(int)));
-    connect(grooveWidget, SIGNAL(newGrooveVelocity(int)), 
-            arpWidget->screen, SLOT(setGrooveVelocity(int)));
-    connect(grooveWidget, SIGNAL(newGrooveLength(int)), 
-            arpWidget->screen, SLOT(setGrooveLength(int)));
-            
-    widgetID = arpData->arpWidgetCount();
-    arpWidget->name = name;
-    arpWidget->ID = widgetID;
-    
-    arpData->addArpWidget(arpWidget);
-    arpData->seqDriver->sendGroove();
-    
+    connect(moduleWidget, SIGNAL(setMidiLearn(int, int, int)),
+            engine, SLOT(setMidiLearn(int, int, int)));
+
+    connect(grooveWidget, SIGNAL(newGrooveTick(int)),
+            moduleWidget->screen, SLOT(setGrooveTick(int)));
+    connect(grooveWidget, SIGNAL(newGrooveVelocity(int)),
+            moduleWidget->screen, SLOT(setGrooveVelocity(int)));
+    connect(grooveWidget, SIGNAL(newGrooveLength(int)),
+            moduleWidget->screen, SLOT(setGrooveLength(int)));
+
+    widgetID = engine->arpWidgetCount();
+    moduleWidget->name = name;
+    moduleWidget->ID = widgetID;
+
+    engine->addArpWidget(moduleWidget);
+    engine->sendGroove();
+
     QDockWidget *moduleWindow = new QDockWidget(name, this);
     moduleWindow->setFeatures(QDockWidget::DockWidgetMovable
             | QDockWidget::DockWidgetFloatable);
-    moduleWindow->setWidget(arpWidget);
+    moduleWindow->setWidget(moduleWidget);
     moduleWindow->setObjectName(name);
     addDockWidget(Qt::TopDockWidgetArea, moduleWindow);
     if (passWidget->compactStyle) moduleWindow->setStyleSheet(COMPACT_STYLE);
-    
-    count = arpData->moduleWindowCount();
-    arpWidget->parentDockID = count;
-    if (count) tabifyDockWidget(arpData->moduleWindow(count - 1), moduleWindow);
-    arpData->addModuleWindow(moduleWindow);
+
+    count = engine->moduleWindowCount();
+    moduleWidget->parentDockID = count;
+    if (count) tabifyDockWidget(engine->moduleWindow(count - 1), moduleWindow);
+    engine->addModuleWindow(moduleWindow);
     moduleWindow->show();
     checkIfFirstModule();
 }
@@ -383,126 +405,130 @@ void MainWindow::addArp(const QString& name)
 void MainWindow::addLfo(const QString& name)
 {
     int count, widgetID;
-    MidiLfo *midiLfo = new MidiLfo();
-    arpData->addMidiLfo(midiLfo);   
-    LfoWidget *lfoWidget = new LfoWidget(midiLfo,
-            arpData->getPortCount(), passWidget->compactStyle, this);
-    connect(lfoWidget, SIGNAL(lfoRemove(int)), 
+    MidiLfo *midiWorker = new MidiLfo();
+    engine->addMidiLfo(midiWorker);
+    LfoWidget *moduleWidget = new LfoWidget(midiWorker,
+            engine->getPortCount(), passWidget->compactStyle, this);
+    connect(midiWorker, SIGNAL(nextStep(int)),
+            moduleWidget, SLOT(updateScreen(int)));
+    connect(moduleWidget, SIGNAL(moduleRemove(int)),
             this, SLOT(removeLfo(int)));
-    connect(lfoWidget, SIGNAL(dockRename(const QString&, int)), 
+    connect(moduleWidget, SIGNAL(dockRename(const QString&, int)),
             this, SLOT(renameDock(const QString&, int)));
-    connect(lfoWidget, SIGNAL(setMidiLearn(int, int, int)), 
-            arpData, SLOT(setMidiLearn(int, int, int)));
+    connect(moduleWidget, SIGNAL(setMidiLearn(int, int, int)),
+            engine, SLOT(setMidiLearn(int, int, int)));
 
-    widgetID = arpData->lfoWidgetCount();
-    lfoWidget->name = name;
-    lfoWidget->ID = widgetID;
+    widgetID = engine->lfoWidgetCount();
+    moduleWidget->name = name;
+    moduleWidget->ID = widgetID;
 
-    arpData->addLfoWidget(lfoWidget);
+    engine->addLfoWidget(moduleWidget);
 
     QDockWidget *moduleWindow = new QDockWidget(name, this);
     moduleWindow->setFeatures(QDockWidget::DockWidgetMovable
             | QDockWidget::DockWidgetFloatable);
-    moduleWindow->setWidget(lfoWidget);
+    moduleWindow->setWidget(moduleWidget);
     moduleWindow->setObjectName(name);
     addDockWidget(Qt::TopDockWidgetArea, moduleWindow);
     if (passWidget->compactStyle) moduleWindow->setStyleSheet(COMPACT_STYLE);
-    
-    count = arpData->moduleWindowCount();
-    lfoWidget->parentDockID = count;
-    if (count) tabifyDockWidget(arpData->moduleWindow(count - 1), moduleWindow);
-    arpData->addModuleWindow(moduleWindow);
+
+    count = engine->moduleWindowCount();
+    moduleWidget->parentDockID = count;
+    if (count) tabifyDockWidget(engine->moduleWindow(count - 1), moduleWindow);
+    engine->addModuleWindow(moduleWindow);
     checkIfFirstModule();
 }
 
 void MainWindow::addSeq(const QString& name)
 {
     int count, widgetID;
-    MidiSeq *midiSeq = new MidiSeq();
-    arpData->addMidiSeq(midiSeq);   
-    SeqWidget *seqWidget = new SeqWidget(midiSeq,
-            arpData->getPortCount(), passWidget->compactStyle, this);
-    connect(seqWidget, SIGNAL(seqRemove(int)), this, SLOT(removeSeq(int)));
-    connect(seqWidget, SIGNAL(dockRename(const QString&, int)), 
+    MidiSeq *midiWorker = new MidiSeq();
+    engine->addMidiSeq(midiWorker);
+    SeqWidget *moduleWidget = new SeqWidget(midiWorker,
+            engine->getPortCount(), passWidget->compactStyle, this);
+    connect(midiWorker, SIGNAL(nextStep(int)),
+            moduleWidget->screen, SLOT(updateScreen(int)));
+    connect(moduleWidget, SIGNAL(moduleRemove(int)), this, SLOT(removeSeq(int)));
+    connect(moduleWidget, SIGNAL(dockRename(const QString&, int)),
             this, SLOT(renameDock(const QString&, int)));
-    connect(seqWidget, SIGNAL(setMidiLearn(int, int, int)),
-            arpData, SLOT(setMidiLearn(int, int, int)));
-    connect(arpData->seqDriver, SIGNAL(noteEvent(int, int)),
-            seqWidget, SLOT(processNote(int, int)));
-            
-    widgetID = arpData->seqWidgetCount();
-    seqWidget->name = name;
-    seqWidget->ID = widgetID;
-    
-    arpData->addSeqWidget(seqWidget);
-    
+    connect(moduleWidget, SIGNAL(setMidiLearn(int, int, int)),
+            engine, SLOT(setMidiLearn(int, int, int)));
+    connect(midiWorker, SIGNAL(noteEvent(int, int)),
+            moduleWidget, SLOT(processNote(int, int)));
+
+    widgetID = engine->seqWidgetCount();
+    moduleWidget->name = name;
+    moduleWidget->ID = widgetID;
+
+    engine->addSeqWidget(moduleWidget);
+
     QDockWidget *moduleWindow = new QDockWidget(name, this);
     moduleWindow->setFeatures(QDockWidget::DockWidgetMovable
             | QDockWidget::DockWidgetFloatable);
-    moduleWindow->setWidget(seqWidget);
+    moduleWindow->setWidget(moduleWidget);
     moduleWindow->setObjectName(name);
     addDockWidget(Qt::TopDockWidgetArea, moduleWindow);
     if (passWidget->compactStyle) moduleWindow->setStyleSheet(COMPACT_STYLE);
 
-    count = arpData->moduleWindowCount();
-    seqWidget->parentDockID = count;
-    if (count) tabifyDockWidget(arpData->moduleWindow(count - 1), moduleWindow);
-    arpData->addModuleWindow(moduleWindow);
+    count = engine->moduleWindowCount();
+    moduleWidget->parentDockID = count;
+    if (count) tabifyDockWidget(engine->moduleWindow(count - 1), moduleWindow);
+    engine->addModuleWindow(moduleWindow);
 
     checkIfFirstModule();
 }
 
-void MainWindow::renameDock(const QString& name, int parentDockID) 
+void MainWindow::renameDock(const QString& name, int parentDockID)
 {
-    arpData->moduleWindow(parentDockID)->setWindowTitle(name);
-    arpData->setModified(true);
+    engine->moduleWindow(parentDockID)->setWindowTitle(name);
+    engine->setModified(true);
 }
 
 void MainWindow::removeArp(int index)
 {
     int parentDockID;
-    ArpWidget *arpWidget = arpData->arpWidget(index);
-    
+    ArpWidget *arpWidget = engine->arpWidget(index);
+
     parentDockID = arpWidget->parentDockID;
-    QDockWidget *dockWidget = arpData->moduleWindow(parentDockID);
-    
-    arpData->removeMidiArp(arpWidget->getMidiWorker());
-    arpData->removeArpWidget(arpWidget);
+    QDockWidget *dockWidget = engine->moduleWindow(parentDockID);
+
+    engine->removeMidiArp(arpWidget->getMidiWorker());
+    engine->removeArpWidget(arpWidget);
     delete arpWidget;
-    arpData->removeModuleWindow(dockWidget);
-    arpData->updateIDs(parentDockID);
+    engine->removeModuleWindow(dockWidget);
+    engine->updateIDs(parentDockID);
     checkIfLastModule();
 }
 
 void MainWindow::removeLfo(int index)
 {
     int parentDockID;
-    LfoWidget *lfoWidget = arpData->lfoWidget(index);
-    
+    LfoWidget *lfoWidget = engine->lfoWidget(index);
+
     parentDockID = lfoWidget->parentDockID;
-    QDockWidget *dockWidget = arpData->moduleWindow(parentDockID);
-    
-    arpData->removeMidiLfo(lfoWidget->getMidiWorker());
-    arpData->removeLfoWidget(lfoWidget);
+    QDockWidget *dockWidget = engine->moduleWindow(parentDockID);
+
+    engine->removeMidiLfo(lfoWidget->getMidiWorker());
+    engine->removeLfoWidget(lfoWidget);
     delete lfoWidget;
-    arpData->removeModuleWindow(dockWidget);
-    arpData->updateIDs(parentDockID);
+    engine->removeModuleWindow(dockWidget);
+    engine->updateIDs(parentDockID);
     checkIfLastModule();
 }
 
 void MainWindow::removeSeq(int index)
 {
     int parentDockID;
-    SeqWidget *seqWidget = arpData->seqWidget(index);
-    
+    SeqWidget *seqWidget = engine->seqWidget(index);
+
     parentDockID = seqWidget->parentDockID;
-    QDockWidget *dockWidget = arpData->moduleWindow(parentDockID);
-    
-    arpData->removeMidiSeq(seqWidget->getMidiWorker());
-    arpData->removeSeqWidget(seqWidget);
+    QDockWidget *dockWidget = engine->moduleWindow(parentDockID);
+
+    engine->removeMidiSeq(seqWidget->getMidiWorker());
+    engine->removeSeqWidget(seqWidget);
     delete seqWidget;
-    arpData->removeModuleWindow(dockWidget);
-    arpData->updateIDs(parentDockID);
+    engine->removeModuleWindow(dockWidget);
+    engine->updateIDs(parentDockID);
     checkIfLastModule();
 }
 
@@ -510,17 +536,17 @@ void MainWindow::clear()
 {
     updateRunQueue(false);
     jackSyncToggle(false);
-    
-    while (arpData->midiArpCount()) {
-        removeArp(arpData->midiArpCount() - 1);
+
+    while (engine->midiArpCount()) {
+        removeArp(engine->midiArpCount() - 1);
     }
 
-    while (arpData->midiLfoCount()) {
-        removeLfo(arpData->midiLfoCount() - 1);
+    while (engine->midiLfoCount()) {
+        removeLfo(engine->midiLfoCount() - 1);
     }
-    
-    while (arpData->midiSeqCount()) {
-        removeSeq(arpData->midiSeqCount() - 1);
+
+    while (engine->midiSeqCount()) {
+        removeSeq(engine->midiSeqCount() - 1);
     }
 }
 
@@ -530,7 +556,7 @@ void MainWindow::fileNew()
         clear();
         filename = "";
         updateWindowTitle();
-        arpData->setModified(false);
+        engine->setModified(false);
     }
 }
 
@@ -548,10 +574,10 @@ void MainWindow::chooseFile()
             + tr("Old QMidiArp files") + " (*.qma)");
     if (fn.isEmpty())
         return;
-        
+
     if (fn.endsWith(".qma"))
         openTextFile(fn);
-        
+
     else if (fn.endsWith(FILEEXT))
         openFile(fn);
 }
@@ -578,7 +604,7 @@ void MainWindow::openFile(const QString& fn)
         if (xml.isStartElement()) {
             if (xml.isEndElement())
                 break;
-            
+
             if (xml.name() != "session") {
                 xml.raiseError(tr("Not a QMidiArp xml file."));
                 QMessageBox::warning(this, APP_NAME,
@@ -587,11 +613,11 @@ void MainWindow::openFile(const QString& fn)
             }
             while (!xml.atEnd()) {
                 xml.readNext();
-            
+
                 if (xml.isEndElement())
                     break;
-                
-                if ((xml.isStartElement()) && (xml.name() == "global")) 
+
+                if ((xml.isStartElement()) && (xml.name() == "global"))
                     readFilePartGlobal(xml);
                 else if (xml.isStartElement() && (xml.name() == "modules"))
                     readFilePartModules(xml);
@@ -604,11 +630,11 @@ void MainWindow::openFile(const QString& fn)
     }
 
     addRecentlyOpenedFile(filename, recentFiles);
-    arpData->setModified(false);    
+    engine->setModified(false);
 }
 
 void MainWindow::readFilePartGlobal(QXmlStreamReader& xml)
-{    
+{
     while (!xml.atEnd()) {
         xml.readNext();
         if (xml.isEndElement()) {
@@ -631,7 +657,7 @@ void MainWindow::readFilePartGlobal(QXmlStreamReader& xml)
                 else if (xml.name() == "forwardUnmatched")
                     passWidget->setForward(xml.readElementText().toInt());
                 else if (xml.name() == "forwardPort")
-                    passWidget->setPortUnmatched(xml.readElementText().toInt() + 1);
+                    passWidget->setPortUnmatched(xml.readElementText().toInt());
                 else skipXmlElement(xml);
             }
         }
@@ -661,17 +687,17 @@ void MainWindow::readFilePartModules(QXmlStreamReader& xml)
             break;
         if (xml.isStartElement() && (xml.name() == "Arp")) {
             addArp("Arp:" + xml.attributes().value("name").toString());
-            arpData->arpWidget(arpData->midiArpCount() - 1)
+            engine->arpWidget(engine->midiArpCount() - 1)
                     ->readData(xml);
-        }   
+        }
         else if (xml.isStartElement() && (xml.name() == "LFO")) {
             addLfo("LFO:" + xml.attributes().value("name").toString());
-            arpData->lfoWidget(arpData->midiLfoCount() - 1)
+            engine->lfoWidget(engine->midiLfoCount() - 1)
                     ->readData(xml);
-        }   
+        }
         else if (xml.isStartElement() && (xml.name() == "Seq")) {
             addSeq("Seq:" + xml.attributes().value("name").toString());
-            arpData->seqWidget(arpData->midiSeqCount() - 1)
+            engine->seqWidget(engine->midiSeqCount() - 1)
                     ->readData(xml);
         }
         else skipXmlElement(xml);
@@ -698,10 +724,10 @@ void MainWindow::skipXmlElement(QXmlStreamReader& xml)
         qWarning("Unknown Element in XML File: %s",qPrintable(xml.name().toString()));
         while (!xml.atEnd()) {
             xml.readNext();
-    
+
             if (xml.isEndElement())
                 break;
-    
+
             if (xml.isStartElement()) {
                 skipXmlElement(xml);
             }
@@ -757,14 +783,14 @@ void MainWindow::openTextFile(const QString& fn)
     qs2 = qs.section(' ', 0, 0);
 
     grooveWidget->grooveTick->setValue(qs2.toInt());
-    //  arpData->seqDriver->setGrooveTick(qs2.toInt());
+    //  engine->seqDriver->setGrooveTick(qs2.toInt());
     qs2 = qs.section(' ', 1, 1);
     grooveWidget->grooveVelocity->setValue(qs2.toInt());
-    //  arpData->seqDriver->setGrooveVelocity(qs2.toInt());
+    //  engine->seqDriver->setGrooveVelocity(qs2.toInt());
     qs2 = qs.section(' ', 2, 2);
     grooveWidget->grooveLength->setValue(qs2.toInt());
-    //  arpData->seqDriver->setGrooveLength(qs2.toInt());
- 
+    //  engine->seqDriver->setGrooveLength(qs2.toInt());
+
     while (!loadText.atEnd()) {
         qs = loadText.readLine();
         if (qs.startsWith("GUI"))
@@ -779,20 +805,20 @@ void MainWindow::openTextFile(const QString& fn)
         switch (c) {
             case 1:
                 addSeq(qs);
-                arpData->seqWidget(arpData->midiSeqCount() - 1)->readDataText(loadText);
+                engine->seqWidget(engine->midiSeqCount() - 1)->readDataText(loadText);
             break;
             case 2:
                 addLfo(qs);
-                arpData->lfoWidget(arpData->midiLfoCount() - 1)->readDataText(loadText);
+                engine->lfoWidget(engine->midiLfoCount() - 1)->readDataText(loadText);
             break;
             case 3:
                 addArp(qs);
-                arpData->arpWidget(arpData->midiArpCount() - 1)->readDataText(loadText);
+                engine->arpWidget(engine->midiArpCount() - 1)->readDataText(loadText);
             break;
             default:
                 qs = "Arp: " + qs;
                 addArp(qs);
-                arpData->arpWidget(arpData->midiArpCount() - 1)->readDataText(loadText);
+                engine->arpWidget(engine->midiArpCount() - 1)->readDataText(loadText);
             break;
         }
     }
@@ -802,14 +828,14 @@ void MainWindow::openTextFile(const QString& fn)
         QByteArray array = QByteArray::fromHex(qs.toLatin1());
         restoreState(array);
     }
-    
+
     midiClockAction->setChecked(midiclocktmp);
-    
+
     filename.append("x");
     QMessageBox::warning(this, APP_NAME,
             tr("The QMidiArp text file was imported. If you save this file, \
 it will be saved using the newer xml format under the name\n '%1'.").arg(filename));
-    
+
     updateWindowTitle();
 }
 
@@ -822,7 +848,7 @@ void MainWindow::fileSave()
 }
 
 bool MainWindow::saveFile()
-{    
+{
     int l1;
     int ns = 0;
     int nl = 0;
@@ -830,7 +856,7 @@ bool MainWindow::saveFile()
 
     QFile f(filename);
     QString nameTest;
-    
+
     if (!f.open(QIODevice::WriteOnly)) {
         QMessageBox::warning(this, APP_NAME,
                 tr("Could not write to file '%1'.").arg(filename));
@@ -844,123 +870,66 @@ bool MainWindow::saveFile()
     xml.writeAttribute("version", PACKAGE_VERSION);
     xml.writeAttribute("name", filename.mid(filename.lastIndexOf('/') + 1,
                     filename.count() - filename.lastIndexOf('/') - 6));
-    
+
     xml.writeStartElement("global");
-    
+
         xml.writeTextElement("tempo", QString::number(tempoSpin->value()));
-        
-        xml.writeStartElement("settings");    
-            xml.writeTextElement("midiControlEnabled", 
+
+        xml.writeStartElement("settings");
+            xml.writeTextElement("midiControlEnabled",
                 QString::number((int)passWidget->cbuttonCheck->isChecked()));
-            xml.writeTextElement("midiClockEnabled", 
-                QString::number((int)arpData->seqDriver->use_midiclock));
-            xml.writeTextElement("jackSyncEnabled", 
-                QString::number((int)arpData->seqDriver->use_jacksync));
-            xml.writeTextElement("forwardUnmatched", 
-                QString::number((int)arpData->seqDriver->forwardUnmatched));
-            xml.writeTextElement("forwardPort", 
-                QString::number(arpData->seqDriver->portUnmatched));
+            xml.writeTextElement("midiClockEnabled",
+                QString::number((int)engine->seqDriver->use_midiclock));
+            xml.writeTextElement("jackSyncEnabled",
+                QString::number((int)engine->seqDriver->use_jacksync));
+            xml.writeTextElement("forwardUnmatched",
+                QString::number((int)engine->seqDriver->forwardUnmatched));
+            xml.writeTextElement("forwardPort",
+                QString::number(engine->seqDriver->portUnmatched));
         xml.writeEndElement();
-        
+
         xml.writeStartElement("groove");
-            xml.writeTextElement("tick", 
-                QString::number(arpData->seqDriver->grooveTick));
-            xml.writeTextElement("velocity", 
-                QString::number(arpData->seqDriver->grooveVelocity));
-            xml.writeTextElement("length", 
-                QString::number(arpData->seqDriver->grooveLength));
+            xml.writeTextElement("tick",
+                QString::number(engine->grooveTick));
+            xml.writeTextElement("velocity",
+                QString::number(engine->grooveVelocity));
+            xml.writeTextElement("length",
+                QString::number(engine->grooveLength));
         xml.writeEndElement();
-        
+
     xml.writeEndElement();
-   
+
     xml.writeStartElement("modules");
-  
-    for (l1 = 0; l1 < arpData->moduleWindowCount(); l1++) {
-        
-        nameTest = arpData->moduleWindow(l1)->objectName();
-        
+
+    for (l1 = 0; l1 < engine->moduleWindowCount(); l1++) {
+
+        nameTest = engine->moduleWindow(l1)->objectName();
+
         if (nameTest.startsWith('S')) {
-            arpData->seqWidget(ns)->writeData(xml);
+            engine->seqWidget(ns)->writeData(xml);
             ns++;
-        } 
+        }
         if (nameTest.startsWith('L')) {
-            arpData->lfoWidget(nl)->writeData(xml);
+            engine->lfoWidget(nl)->writeData(xml);
             nl++;
         }
         if (nameTest.startsWith('A')) {
-            arpData->arpWidget(na)->writeData(xml);
+            engine->arpWidget(na)->writeData(xml);
             na++;
         }
     }
-    
+
     xml.writeEndElement();
-    
-    xml.writeStartElement("GUI");    
+
+    xml.writeStartElement("GUI");
         xml.writeTextElement("windowState", saveState().toHex());
     xml.writeEndElement();
-    
+
     xml.writeEndElement();
     xml.writeEndDocument();
-    
-
-    arpData->setModified(false);
-    return true;
-}
 
-bool MainWindow::saveTextFile()
-{
-    int l1;
-    int ns = 0;
-    int nl = 0;
-    int na = 0;
-    
-    QString nameTest;
-    QFile f(filename);
 
-    if (!f.open(QIODevice::WriteOnly)) {
-        QMessageBox::warning(this, APP_NAME,
-                tr("Could not write to file '%1'.").arg(filename));
-        return false;
-    }
-    
-    
-    QTextStream saveText(&f);
-    saveText << "Tempo\n";   
-    saveText << tempoSpin->value() << '\n';    
-    saveText << "MIDI Control\n";
-    saveText << (int)passWidget->cbuttonCheck->isChecked() << '\n';
-    saveText << "MIDI Clock\n";
-    saveText << (int)arpData->seqDriver->use_midiclock << '\n';
-    saveText << (int)arpData->seqDriver->forwardUnmatched;
-    saveText << ' ' << arpData->seqDriver->portUnmatched << '\n';
-    
-    saveText << arpData->seqDriver->grooveTick;
-    saveText << ' ' << arpData->seqDriver->grooveVelocity;
-    saveText << ' ' << arpData->seqDriver->grooveLength << '\n';
-
-    for (l1 = 0; l1 < arpData->moduleWindowCount(); l1++) {
-        
-        nameTest = arpData->moduleWindow(l1)->objectName();
-        
-        if (nameTest.startsWith('S')) {
-            saveText << qPrintable(arpData->seqWidget(ns)->name) << '\n';
-            arpData->seqWidget(ns)->writeDataText(saveText);
-            ns++;
-        } 
-        if (nameTest.startsWith('L')) {
-            saveText << qPrintable(arpData->lfoWidget(nl)->name) << '\n';
-            arpData->lfoWidget(nl)->writeDataText(saveText);
-            nl++;
-        }
-        if (nameTest.startsWith('A')) {
-            saveText << qPrintable(arpData->arpWidget(na)->name) << '\n';
-            arpData->arpWidget(na)->writeDataText(saveText);
-            na++;
-        }
-    }
-    saveText << "GUI Settings\n";
-    saveText << saveState().toHex();
-    arpData->setModified(false);
+    engine->setModified(false);
     return true;
 }
 
@@ -974,7 +943,7 @@ bool MainWindow::saveFileAs()
     bool result = false;
 
     QString fn =  QFileDialog::getSaveFileName(this,
-            tr("Save arpeggiator"), lastDir, tr("QMidiArp files") 
+            tr("Save arpeggiator"), lastDir, tr("QMidiArp files")
             + " (*" + FILEEXT + ")");
 
     if (!fn.isEmpty()) {
@@ -1032,19 +1001,19 @@ void MainWindow::closeEvent(QCloseEvent* e)
     if (isSave()) {
         writeRcFile();
         e->accept();
-    } else 
+    } else
         e->ignore();
 }
 
 bool MainWindow::isModified()
 {
-    return arpData->isModified();
+    return engine->isModified();
 }
 
 void MainWindow::updateTempo(int p_tempo)
 {
-    arpData->seqDriver->setQueueTempo(p_tempo);
-    arpData->setModified(true);
+    engine->seqDriver->setQueueTempo(p_tempo);
+    engine->setModified(true);
 }
 
 void MainWindow::updateRunQueue(bool on)
@@ -1055,13 +1024,13 @@ void MainWindow::updateRunQueue(bool on)
 
 void MainWindow::resetQueue()
 {
-    arpData->seqDriver->runQueue(arpData->seqDriver->runArp);
+    engine->seqDriver->setQueueStatus(engine->seqDriver->runArp);
 }
 
 void MainWindow::midiClockToggle(bool on)
 {
     if (on) jackSyncAction->setChecked(false);
-    arpData->seqDriver->setUseMidiClock(on);
+    engine->seqDriver->setUseMidiClock(on);
     setGUIforExtSync(on);
 }
 
@@ -1069,7 +1038,7 @@ void MainWindow::jackSyncToggle(bool on)
 {
     if (on) midiClockAction->setChecked(false);
     setGUIforExtSync(on);
-    arpData->seqDriver->setUseJackTransport(on);
+    engine->seqDriver->setUseJackTransport(on);
 }
 
 void MainWindow::setGUIforExtSync(bool on)
@@ -1081,7 +1050,7 @@ void MainWindow::setGUIforExtSync(bool on)
     addSeqAction->setDisabled(on);
     fileOpenAction->setDisabled(on);
     fileRecentlyOpenedFiles->setDisabled(on);
-}   
+}
 
 bool MainWindow::checkRcFile()
 {
@@ -1091,15 +1060,15 @@ bool MainWindow::checkRcFile()
 
         patternNames
             <<  "                         "
-            <<  "Simple 4"   
-            <<  "Simple 8"   
-            <<  "Simple 16"  
-            <<  "Simple 32"  
-            <<  "Chord 8"    
-            <<  "Chord+Bass 16"   
-            <<  "Chord Oct 16 A"  
-            <<  "Chord Oct 16 B"  
-            <<  "Chord Oct 16 C"  
+            <<  "Simple 4"
+            <<  "Simple 8"
+            <<  "Simple 16"
+            <<  "Simple 32"
+            <<  "Chord 8"
+            <<  "Chord+Bass 16"
+            <<  "Chord Oct 16 A"
+            <<  "Chord Oct 16 B"
+            <<  "Chord Oct 16 C"
             <<  "Chords/Glissando 16";
 
         patternPresets
@@ -1114,7 +1083,7 @@ bool MainWindow::checkRcFile()
             << ">>///0\\ \\ \\ 0+////0\\ \\ \\ \\ -00+0-00+0-00+0-00+0-0"
             << ">>///0\\ \\ \\ 0+////(0123)\\ \\ \\ \\ -00+(1234)-00+0-00+0-00+0-0"
             << "d(012)>h(123)>d(012)<d(234)>hh(23)(42)(12)(43)>d012342";
-            
+
         writeRcFile();
         retval = false;
     }
@@ -1125,7 +1094,7 @@ void MainWindow::readRcFile()
 {
     QString qs;
     QStringList value;
-    
+
     QDir qmahome = QDir(QDir::homePath());
     QString qmarcpath = qmahome.filePath(QMARCNAME);
     QFile f(qmarcpath);
@@ -1134,14 +1103,14 @@ void MainWindow::readRcFile()
         QMessageBox::warning(this, PACKAGE,
                 tr("Could not read from resource file"));
         return;
-    }   
+    }
     QTextStream loadText(&f);
     patternNames.clear();
     patternPresets.clear();
 
     while (!loadText.atEnd()) {
         qs = loadText.readLine();
-        
+
         if (qs.startsWith('#')) {
             value.clear();
             value = qs.split('%');
@@ -1179,14 +1148,14 @@ void MainWindow::writeRcFile()
         return;
     }
     QTextStream writeText(&f);
-    
-    for (l1 = 0; l1 < patternNames.count(); l1++) 
+
+    for (l1 = 0; l1 < patternNames.count(); l1++)
     {
         writeText << "#Pattern%";
         writeText << qPrintable(patternNames.at(l1)) << "%";
         writeText << qPrintable(patternPresets.at(l1)) << endl;
     }
-    
+
     writeText << "#CompactStyle%";
     writeText << passWidget->compactStyle << endl;
     writeText << "#EnableLog%";
@@ -1198,7 +1167,7 @@ void MainWindow::writeRcFile()
 
     writeText << "#LastDir%";
     writeText << lastDir << endl;
-    
+
     // save recently opened files (all recent files code taken from AMS)
     if (recentFiles.count() > 0) {
         QStringList::Iterator it = recentFiles.begin();
@@ -1254,7 +1223,6 @@ void MainWindow::appendRecentlyOpenedFile(const QString &fn, QStringList &lst)
     lst.append(fi.absoluteFilePath());
 }
 
-
 void MainWindow::updatePatternPresets(const QString& n, const QString& p,
         int index)
 {
@@ -1266,13 +1234,13 @@ void MainWindow::updatePatternPresets(const QString& n, const QString& p,
         patternNames.append(n);
         patternPresets.append(p);
     }
-    arpData->updatePatternPresets(n, p, index);
+    engine->updatePatternPresets(n, p, index);
     writeRcFile();
 }
 
 void MainWindow::checkIfLastModule()
 {
-    if (!arpData->moduleWindowCount()) {
+    if (!engine->moduleWindowCount()) {
         runAction->setDisabled(true);
         runAction->setChecked(false);
         midiClockAction->setDisabled(true);
@@ -1284,10 +1252,10 @@ void MainWindow::checkIfLastModule()
 
 void MainWindow::checkIfFirstModule()
 {
-    if (arpData->moduleWindowCount() == 1) {
+    if (engine->moduleWindowCount() == 1) {
         midiClockAction->setEnabled(true);
         jackSyncAction->setEnabled(true);
-        runAction->setEnabled(!(midiClockAction->isChecked() 
+        runAction->setEnabled(!(midiClockAction->isChecked()
                                 || jackSyncAction->isChecked()));
     }
 }
@@ -1298,9 +1266,6 @@ void MainWindow::showMidiCCDialog()
     midiCCTable->show();
 }
 
-/* Handler for system signals (SIGUSR1, SIGINT...)
- * Write a message to the pipe and leave as soon as possible
- */
 void MainWindow::handleSignal(int sig)
 {
     if (write(sigpipe[1], &sig, sizeof(sig)) == -1) {
@@ -1340,7 +1305,6 @@ bool MainWindow::installSignalHandlers()
     return true;
 }
 
-/* Slot to give response to the incoming pipe message */
 void MainWindow::signalAction(int fd)
 {
     int message;
@@ -1349,7 +1313,7 @@ void MainWindow::signalAction(int fd)
         qWarning("read() failed: %s", std::strerror(errno));
         return;
     }
-    
+
     switch (message) {
         case SIGUSR1:
             fileSave();
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 7461d1c..105d3f8 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -1,3 +1,28 @@
+/*!
+ * @file mainwindow.h
+ * @brief Member definitions for the MainWindow top-level UI class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
+
 #ifndef MAINWINDOW_H
 #define MAINWINDOW_H
 
@@ -11,7 +36,7 @@
 #include <QToolBar>
 
 #include "logwidget.h"
-#include "arpdata.h"
+#include "engine.h"
 #include "midicctable.h"
 #include "passwidget.h"
 #include "groovewidget.h"
@@ -25,96 +50,448 @@ static const char ABOUTMSG[] = "<html> <p><b><big>" APP_NAME " " PACKAGE_VERSION
               "<a href="http://sourceforge.net/projects/qmidiarp">http://sourceforge.net/projects/qmidiarp</a></p>"
                           APP_NAME " is licensed under the GPL.</b></p></html>";
 
+/*!
+ * The MainWindow class is the main UI that holds functions to manage global
+ * QMidiArp parameters and modules and to load and save parameters to
+ * disk. The constructor sets up all main window elements including
+ * toolbars and menus. It instantiates the LogWidget, PassWidget,
+ * MidiCCTable and their DockWidget windows. It also instantiates the
+ * Engine widget holding the lists of modules.
+
+ * @brief Top-level UI class. Instantiates Engine
+ */
 class MainWindow : public QMainWindow
 {
     Q_OBJECT
 
+  private:
     static int sigpipe[2];
     QSpinBox *tempoSpin;
     PassWidget *passWidget;
     GrooveWidget *grooveWidget;
     LogWidget *logWidget;
-    ArpData *arpData;
+    Engine *engine;
     MidiCCTable *midiCCTable;
     QString lastDir, filename;
     QStringList patternNames, patternPresets;
     QStringList recentFiles;
     QDockWidget *logWindow, *grooveWindow, *passWindow;
-    
+
+    QToolBar *controlToolBar, *fileToolBar;
+    QAction *runAction, *addArpAction;
+    QAction *addLfoAction, *addSeqAction;
+    QAction *fileNewAction, *fileOpenAction, *fileSaveAction, *fileSaveAsAction;
+    QAction *fileQuitAction;
+    QAction *midiClockAction, *jackSyncAction;
+    QMenu* fileRecentlyOpenedFiles;
+
+/*!
+* @brief This function opens a file dialog and calls either
+* MainWindow::openTextFile or
+* MainWindow::openFile depending on the selected file extension.
+* It is called by MainWindow::fileOpen.
+*/
     void chooseFile();
+/*! @brief This function checks whether parameter modifications
+ * were done after the last save.
+ *
+ * If yes, it queries the user how to handle unsaved changes using a
+ * message box.
+ * @return True if the parameters can be overwritten
+ */
     bool isSave();
     void updateWindowTitle();
-    bool saveTextFile();
+/*!
+* @brief This function opens a QMidiArp XML session file named
+* MainWindow::filename for write using QXmlStreamReader.
+*
+* It writes global and GUI parameters and and calls the
+* block writers in the module widgets.
+*
+* @return True if write was successful
+*/
     bool saveFile();
+/*! @brief This function opens a file dialog and appends the file extension to
+ * the chosen name if not present. It is called by fileSaveAs.
+ * @return True if a file name was successfully chosen
+ */
     bool saveFileAs();
+/*!
+* @brief This function returns the result of Engine::isModified
+* @return True if unsaved parameter modifications exist in any module
+*
+*/
     bool isModified();
-    bool seqEventLocked;
 
+/*!
+* @brief This function creates and adds a new MidiArp to Engine.
+*
+* It also creates and adds the associated ArpWidget
+* and DockWidget to the corresponding lists in Engine. It sets
+* the ArpWidget::name and ArpWidget::ID as the current count of the
+* Engine::MidiArpList.
+*
+* @param name Name attribute of the created arpeggiator module
+*/
     void addArp(const QString&);
+/*!
+* @brief This function creates and adds a new MidiLfo to Engine.
+*
+* It also creates and adds the associated LfoWidget
+* and DockWidget to the corresponding lists in Engine. It sets
+* the LfoWidget::name and SeqWidget::ID as the current count of the
+* Engine::MidiLfoList.
+*
+* @param name Name attribute of this LFO module
+*/
     void addLfo(const QString&);
+/*!
+* @brief This function creates and adds a new MidiSeq to Engine.
+*
+* It also creates and adds the associated SeqWidget
+* and DockWidget to the corresponding lists in Engine. It sets
+* the SeqWidget::name and SeqWidget::ID as the current count of the
+* Engine::MidiSeqList.
+*
+* @param name Name attribute of this arpeggiator module
+*/
     void addSeq(const QString&);
 
+/*!
+* @brief This function reads global parameter block from an XML session
+* stream using the QXmlStreamReader passed by the caller.
+*
+* @param xml Reference to QXmlStreamReader containing the open XML stream
+*/
     void readFilePartGlobal(QXmlStreamReader& xml);
+/*!
+* @brief This function reads the module parameter blocks from the XML stream
+*  by calling their read functions.
+*
+* It uses the first three letters of the module name to distinguish their
+* type. It creates the according module components and calls their UI
+* widgets, which fill them with the parameters found in the same stream.
+* Calls Engine->NNNWidget->readData, where NNN is Arp, Lfo or Seq.
+*
+* @param xml Reference to QXmlStreamReader containing the open XML stream
+*/
     void readFilePartModules(QXmlStreamReader& xml);
+/*!
+* @brief This function reads the GUI settings block
+* from the XML session stream passed by the caller.
+*
+* @param xml Reference to QXmlStreamReader containing the XML stream
+*/
     void readFilePartGUI(QXmlStreamReader& xml);
+/*!
+* @brief This function prepends a filename at the beginning of the recently
+* opened files list.
+*
+* It is called by openFile if the opening was successful.
+* @param fn Filename with full path to be prepended
+* @param lst The list of recently opened files
+* @see appendRecentlyOpenedFile, setupRecentFilesMenu
+*/
     void addRecentlyOpenedFile(const QString &fn, QStringList &lst);
+/*!
+* @brief This function appends a filename at the end of the recently
+* opened files list.
+*
+* It is called by openFile if the opening was successful.
+* @param fn Filename with full path to be appended
+* @param lst The list of recently opened files
+* @see addRecentlyOpenedFile, setupRecentFilesMenu
+*/
     void appendRecentlyOpenedFile(const QString &fn, QStringList &lst);
+/*!
+* @brief This function checks whether a .qmidiarprc file is present
+* and creates it with default settings, if not.
+* @return True if the file existed, False if it was created now.
+* @see readRcFile, updatePatternPresets
+*
+*/
     bool checkRcFile();
+/*!
+* @brief This function writes the .qmidiarprc text resource file.
+*
+* It is called on program exit and upon modification of the
+* Arp preset list.
+* @see readRcFile, updatePatternPresets
+*/
     void writeRcFile();
+/*!
+* @brief This function reads all elements from the .qmidiarprc text
+* resource file.
+*
+* The file contains the Arp preset patterns, the last
+* GUI state, settings made in the Settings dialog (PassWidget) and
+* the recent files and path.
+* This function is called from the MainWindow constructor.
+* @see readRcFile, updatePatternPresets
+*/
     void readRcFile();
+/*!
+* @brief This function checks if there are no more modules present and sets
+* some GUI elements accordingly if so.
+*
+* It is called by removeArp, removeSeq and removeLfo.
+* @see checkIfFirstModule
+*/
     void checkIfLastModule();
+/*!
+* @brief This function checks if there were no modules present, i.e.
+* if the module we just created is the first one, and sets some GUI
+* elements accordingly if so.
+*
+* It is called by addArp, addSeq and addLfo.
+* @see checkIfLastModule
+*/
     void checkIfFirstModule();
+/*!
+* @brief This function removes and deletes all modules from the
+* lists.
+*
+* It removes all module components, i.e. the Midi workers, UI widgets
+* and DockWidgets. It disconnects jack transport and stops the
+* ALSA Queue if running.
+*/
     void clear();
+/*!
+* @brief This function disables or enables GUI elements depending on
+* synchronization mode
+*
+* It distinguishes between internal and external (MIDI Clock or
+* JACK Transport) sync. This is necessary since some operations would
+* break the synchronization, and some others would become useless.
+* @param on True sets the GUI for external synchronization, False
+* returns it to internal clock state.
+*
+* @see midiClockToggle, jackSyncToggle
+*/
+    void setGUIforExtSync(bool on);
+/*!
+* @brief This function restarts the Queue if it is running and does
+* nothing if it is stopped
+*
+*/
+    void resetQueue();
+
+/*! @brief Handler for system signals (SIGUSR1, SIGINT...).
+ * This function writes a message to the pipe and leaves as soon as possible
+ */
     static void handleSignal(int);
+/*! @brief This function sets up a QSocketNotifier forwarding UNIX signals
+ * as Qt signals to provide Ladish L1 support.
+ *
+ * @return True if installation succeeded.
+*/
     bool installSignalHandlers();
-    
-        
+/*!
+* @brief This function allows ignoring one XML element in the XML stream
+* passed by the caller.
+*
+* It advances the stream read-in. It is used to
+* ignore unknown elements for both-ways-compatibility
+*
+* @param xml Reference to QXmlStreamReader containing the open XML stream
+*/
+    void skipXmlElement(QXmlStreamReader& xml);
+
+
   protected:
+/*!
+* @brief Handler for close events either by quit or if the user closes
+* the window.
+*
+*/
     void closeEvent(QCloseEvent*);
 
+/* PUBLIC MEMBERS */
   public:
+/*!
+* @param p_portCount Number of registered ALSA output ports
+*/
     MainWindow(int p_portCount);
     ~MainWindow();
-    QToolBar *controlToolBar, *fileToolBar;
-    QAction *runAction, *addArpAction;
-    QAction *addLfoAction, *addSeqAction;
-    QAction *fileNewAction, *fileOpenAction, *fileSaveAction, *fileSaveAsAction;
-    QAction *fileQuitAction;
-    QAction *midiClockAction, *jackSyncAction;
-    QMenu* fileRecentlyOpenedFiles;
+/*!
+* @brief This function opens a QMidiArp XML session file for reading
+* using QXmlStreamReader.
+*
+* It queries XML block elements and calls the block readers
+* MainWindow::readFilePartGlobal, MainWindow::readFilePartModules,
+* MainWindow::readFilePartGUI. It sets MainWindow::lastDir according to
+* the file path given with fn and calls MainWindow::updateWindowTitle.
+* It updates MainWindow::recentFiles list.
+*
+* @param fn File name to open including its absolute path
+*/
     void openFile(const QString&);
-    void skipXmlElement(QXmlStreamReader& xml);
+/*!
+* @brief This function opens and reads an old QMidiArp .qma text file.
+*
+* It displays a warning for conversion upon next MainWindow::fileSave.
+* It is called by MainWindow::fileOpen.
+* @param fn File name to open including its absolute path
+*/
     void openTextFile(const QString&);
 
-  signals:  
+/* SIGNALS */
+  signals:
     void newTempo(int);
     void runQueue(bool);
 
-  public slots: 
+/* PUBLIC SLOTS */
+  public slots:
+/*!
+* @brief Slot for "New..." UI entries. This function calls MainWindow::clear
+* and empties the current MainWindow::filename.
+*/
     void fileNew();
+/*!
+* @brief Slot for "Open..." UI entries. This function calls MainWindow::isSave
+*  and MainWindow::chooseFile if all changes are in saved state.
+*/
     void fileOpen();
+/*!
+* @brief Slot for file Save GUI elements. This function calls either
+* MainWindow::saveFileAs
+* or MainWindow::saveFile depending on whether the MainWindow::filename is set.
+*
+*/
     void fileSave();
+/*!
+* @brief Slot for file SaveAs GUI elements. This function calls saveFileAs.
+*
+*/
     void fileSaveAs();
-    void recentFileActivated(QAction*);
-    void setupRecentFilesMenu();
+/*!
+* @brief Slot for "Add Arpeggiator" menu entry and toolbutton.
+*
+* It asks for
+* the module name and calls MainWindow::addArp with that name.
+*/
     void arpNew();
+/*!
+* @brief Slot for "Add LFO" menu entry and toolbutton.
+*
+* It asks for
+* the module name and calls MainWindow::addLfo with that name.
+*/
     void lfoNew();
+/*!
+* @brief Slot for "Add Sequencer" menu entry and toolbutton.
+*
+* It asks for
+* the module name and calls MainWindow::addSeq with that name.
+*/
     void seqNew();
+/*!
+* @brief This function renames the TitleBar of a DockWidget
+* with the passed name
+*
+* @param name New name attribute of the DockWidget
+* @param index The widget ID of the DockWidget to rename
+*/
     void renameDock(const QString& name, int index);
+/*!
+* @brief This function removes and deletes an Arpeggiator module.
+*
+* It removes all components MidiArp, ArpWidget and
+* DockWidget from the corresponding lists in Engine.
+*
+* @param index The Engine::midiArpList index of the arpeggiator to remove
+*/
     void removeArp(int index);
+/*!
+* @brief This function removes and deletes an LFO module.
+*
+* It removes all components MidiLfo, LfoWidget and
+* DockWidget from the corresponding lists in Engine.
+*
+* @param index The Engine::midiLfoList index of the LFO to
+*/
     void removeLfo(int index);
+/*!
+* @brief This function removes and deletes a Seq module.
+*
+* It removes all components MidiSeq, SeqWidget and
+* DockWidget from the corresponding lists in Engine.
+*
+* @param index The Engine::MidiSeqList index of the sequencer to remove
+*/
     void removeSeq(int index);
     void helpAbout();
     void helpAboutQt();
+/*! @brief Slot for tempo spinBox changes.
+* This function forwards a new tempo value to SeqDriver.
+* @param tempo The new tempo to be set
+*
+*/
     void updateTempo(int tempo);
+/*! @brief Slot for runQueue ToolButton.
+* This function emits the MainWindow::runQueue signal with the state passed by the caller.
+* @param on True to set Queue to running state or to restart
+*
+*/
     void updateRunQueue(bool on);
+/*! @brief Slot for midiClock ToolButton.
+* This function toggles SeqDriver between MIDI Clock and internal clock
+* operation.
+* @param on True sets SeqDriver to MIDI Clock operation, false returns to
+* internal clock.
+*
+*/
     void midiClockToggle(bool on);
+/*! @brief Slot for jackSync ToolButton.
+* This function toggles SeqDriver between Jack Transport and internal
+* clock operation.
+* @param on True sets seqDriver to JACK Transport operation, false
+* returns to internal clock.
+*
+* @see jackSyncToggle
+*/
     void jackSyncToggle(bool on);
-    void setGUIforExtSync(bool on);
-    void resetQueue();
-    void updatePatternPresets(const QString& n, const QString& p, int index);
+/*! @brief Slot for "Midi Controllers" menu action. This function displays
+ * the MidiCC Dialog window.
+*/
     void showMidiCCDialog();
+/*!
+* @brief This function appends or deletes an Arp pattern preset.
+*
+* If index = 0, it appends an Arp pattern. If index > 0 it deletes
+* the Arp pattern at index from the list.
+* It deploys the new pattern preset list to all Arp modules by calling
+* Engine::updatePatternPresets and writes
+* the new preset list to the resource file.
+* This function is a signal slot for ArpWidget::presetsChanged and
+* called whenever the preset list is modified in one Arp module.
+*
+* @param n Name of the preset to append if index = 0
+* @param p Text sequence of te preset to append if index = 0
+* @param index List index of the preset to delete or 0 to append a preset
+* @see ArpWidget::presetsChanged, Engine::updatePatternPresets
+*/
+    void updatePatternPresets(const QString& n, const QString& p, int index);
+/*! @brief Slot for fileRecentlyOpenedFiles.
+* This function proceeds to open the file selected in the recent
+* files menu.
+*
+* @see recentFileActivated, setupRecentFilesMenu
+*/
+    void recentFileActivated(QAction*);
+/*!
+* @brief This function populates the recent files menu.
+*
+* It is called by the constructor MainWindow::MainWindow
+* @see recentFileActivated, addRecentlyOpenedFile
+*/
+    void setupRecentFilesMenu();/*! @brief Slot to give response to an incoming pipe message (Ladish L1).
+ *
+ * This function calls fileSave upon reception of SIGUSR1 and close upon
+ * reception of SIGINT.
+ * @param fd UNIX signal number
+*/
     void signalAction(int);
 };
-  
+
 #endif
diff --git a/src/midiarp.cpp b/src/midiarp.cpp
index 7134ce9..cd58067 100644
--- a/src/midiarp.cpp
+++ b/src/midiarp.cpp
@@ -1,27 +1,47 @@
+/*!
+ * @file midiarp.cpp
+ * @brief Implements the MidiArp MIDI worker class for the Arpeggiator Module.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #include <cstdlib>
 #include <cstdio>
 #include <QString>
-#include <alsa/asoundlib.h>
 #include "midiarp.h"
 
 
 MidiArp::MidiArp()
 {
-    int l1;
-
-    for (l1 = 0; l1 < 2; l1++) {
+    for (int l1 = 0; l1 < 2; l1++) {
         rangeIn[l1] = (l1) ? 127 : 0;
         indexIn[l1] = (l1) ? 127 : 0;
-    }  
+    }
     chIn = 0;
     portOut = 0;
     channelOut = 0;
     noteBufPtr = 0;
     noteCount = 0;
-    hold = false;
     restartByKbd = false;
     trigByKbd = false;
-    tempo = 1.0;     // tempo relative to global queue tempo
+    stepWidth = 1.0;     // stepWidth relative to global queue stepWidth
     len = 0.5;       // note length
     vel = 0.8;  // velocity relative to global velocity
     noteIndex[0] = 0;
@@ -64,33 +84,28 @@ MidiArp::~MidiArp(){
     wait();
 }
 
-bool MidiArp::isArp(snd_seq_event_t *evIn) {
+void MidiArp::setMuted(bool on)
+{
+    isMuted = on;
+}
+
+bool MidiArp::wantEvent(MidiEvent event) {
 
-    if ((evIn->type != SND_SEQ_EVENT_NOTEON)
-            && (evIn->type != SND_SEQ_EVENT_NOTEOFF)
-            && (evIn->type != SND_SEQ_EVENT_CONTROLLER)) {
-        return(false);
-    }
-    if ((evIn->data.control.channel < chIn)
-            || (evIn->data.control.channel > chIn)) {
-        return(false);
-    }
-    if ((evIn->type == SND_SEQ_EVENT_NOTEON) 
-            || (evIn->type == SND_SEQ_EVENT_NOTEOFF)) {
-        if (((evIn->data.note.note < indexIn[0])
-                    || (evIn->data.note.note > indexIn[1])) 
-                || ((evIn->data.note.velocity < rangeIn[0])
-                    || (evIn->data.note.velocity > rangeIn[1]))) {
+    if (event.channel != chIn) return(false);
+    if ((event.type == EV_CONTROLLER) && (event.data != CT_FOOTSW)) return(false);
+    if (event.type == EV_NOTEON) {
+        if (((event.data < indexIn[0]) || (event.data > indexIn[1]))
+            || ((event.value < rangeIn[0]) || (event.value > rangeIn[1]))) {
             return(false);
         }
     }
     return(true);
 }
 
-void MidiArp::addNote(int note, int velocity, int tick)
+void MidiArp::handleNoteOn(int note, int velocity, int tick)
 {
     mutex.lock();
-    int bufPtr, newBufPtr, l1, l2, l3;
+    int bufPtr, index;
 
     if (!getPressedNoteCount()) {
         purgeLatchBuffer();
@@ -99,53 +114,36 @@ void MidiArp::addNote(int note, int velocity, int tick)
     }
     // modify buffer that is not accessed by arpeggio output
     bufPtr = (noteBufPtr) ? 0 : 1;
-    //note = evIn->data.note.note;
-    if (!noteCount || (note > notes[bufPtr][0][noteCount - 1])) {
-        notes[bufPtr][0][noteCount] = note;
-        notes[bufPtr][1][noteCount] = velocity;
-        notes[bufPtr][2][noteCount] = tick;
-        notes[bufPtr][3][noteCount] = 0;
-        noteCount++;
-    } 
+
+    if (!noteCount || (note > notes[bufPtr][0][noteCount - 1]))
+        index = noteCount;
     else {
-        l1 = 0;
-        while (note > notes[bufPtr][0][l1]) {
-            l1++;
-        }
-        for (l3 = 0; l3 < 4; l3++) {
-            for (l2 = noteCount; l2 > l1; l2--) {
+        index = 0;
+        while (note > notes[bufPtr][0][index]) index++;
+
+        for (int l3 = 0; l3 < 4; l3++) {
+            for (int l2 = noteCount; l2 > index; l2--) {
                 notes[bufPtr][l3][l2] = notes[bufPtr][l3][l2 - 1];
-            }  
+            }
         }
-        notes[bufPtr][0][l1] = note;
-        notes[bufPtr][1][l1] = velocity;
-        notes[bufPtr][2][l1] = tick;
-        notes[bufPtr][3][l1] = 0;
-        noteCount++;
-    } 
-    if (repeatPatternThroughChord == 2) noteOfs = noteCount - 1;
-    newBufPtr = noteBufPtr;
-    noteBufPtr = bufPtr;
-    for (l3 = 0; l3 < 4; l3++) {
-        for (l2 = 0; l2 < noteCount; l2++) {
-            notes[newBufPtr][l3][l2] = notes[bufPtr][l3][l2];
-        }  
     }
-    mutex.unlock();
-}
+    notes[bufPtr][0][index] = note;
+    notes[bufPtr][1][index] = velocity;
+    notes[bufPtr][2][index] = tick;
+    notes[bufPtr][3][index] = 0;
+    noteCount++;
 
-void MidiArp::setMuted(bool on)
-{
-    isMuted = on;
+    if (repeatPatternThroughChord == 2) noteOfs = noteCount - 1;
+    copyNoteBuffer();
+    mutex.unlock();
 }
 
 void MidiArp::handleNoteOff(int note, int tick, int keep_rel)
 {
-    int bufPtr, newBufPtr, l1, l2, l3;
+    int bufPtr, index;
 
     // modify buffer that is not accessed by arpeggio output
     bufPtr = (noteBufPtr) ? 0 : 1;
-    //note = evIn->data.note.note;
     if (!noteCount) {
         return;
     }
@@ -157,134 +155,133 @@ void MidiArp::handleNoteOff(int note, int tick, int keep_rel)
     if (latch_mode) {
         latchBuffer.append(note);
         if (latchBuffer.count() == noteCount) {
-            latchTimer->stop(); 
-        } 
+            latchTimer->stop();
+        }
         else {
             latchTimer->start(200);
         }
         return;
     }
 
-    if ((!keep_rel) || (!release_time)) {   
+    if ((!keep_rel) || (!release_time)) {
         //definitely remove from buffer
         if (note == notes[bufPtr][0][noteCount - 1]) {
-            noteCount--; 
+            //note is on top of buffer: only decrement noteCount
+            noteCount--;
             if (repeatPatternThroughChord == 2) noteOfs = noteCount - 1;
-        } 
-        else {
-            l1 = 0;
-            while ((l1 < noteCount) && (note > notes[bufPtr][0][l1])) l1++;
-                for (l3 = 0; l3 < 4; l3++) {
-                    for (l2 = l1; l2 < noteCount - 1; l2++) {
-                        notes[bufPtr][l3][l2] = notes[bufPtr][l3][l2 + 1];
-                    }
-                }
-                noteCount--;
         }
-    } 
-    else {
-        //mark as released but keep with note off time tick
-        l1 = 0;
-        while ((l1 < noteCount) && (note > notes[bufPtr][0][l1])) l1++;
-        while ((l1 < noteCount) && (notes[bufPtr][3][l1])) l1++;
-        if (note == notes[bufPtr][0][l1]) {
-            notes[bufPtr][3][l1] = 1;
-            notes[bufPtr][2][l1] = tick;
+        else {
+            //note is not on top: take out the note and pull down all above
+            index = 0;
+            while ((index < noteCount) && (note > notes[bufPtr][0][index])) index++;
+            deleteNoteAt(index, bufPtr);
         }
     }
-        newBufPtr = noteBufPtr;
-        noteBufPtr = bufPtr;
-        for (l3 = 0; l3 < 4; l3++) {
-            for (l2 = 0; l2 < noteCount; l2++) {
-                notes[newBufPtr][l3][l2] = notes[bufPtr][l3][l2];
-            }
-        }
+    else tagAsReleased(note, tick, bufPtr);
+
+    copyNoteBuffer();
 }
 
 void MidiArp::removeNote(int *noteptr, int tick, int keep_rel)
 {
-    int bufPtr, newBufPtr, l1, l2, l3, note ;
+    int bufPtr, index, note ;
     note = *noteptr;
-    int tickmark = tick;
-    
+
     // modify buffer that is not accessed by arpeggio output
     bufPtr = (noteBufPtr) ? 0 : 1;
     if (!noteCount) {
         return;
     }
     if (!keep_rel || (!release_time)) {
-        //definitely remove from buffer, do NOT check for doubles
+        // definitely remove from buffer, do NOT check for doubles
         if (note == notes[bufPtr][0][noteCount - 1]) {
-                noteCount--;
-                if ((repeatPatternThroughChord == 2) && (noteOfs)) noteOfs--;
-        } 
+            // note is on top of buffer: only decrement noteCount
+            noteCount--;
+            if ((repeatPatternThroughChord == 2) && (noteOfs)) noteOfs--;
+        }
         else {
-            l1 = 0;
-            while ((l1 < noteCount) && (note > notes[bufPtr][0][l1])) {
-                l1++;
-            }
-            while ((l1 < noteCount) && (!notes[bufPtr][3][l1]) 
-                    && (tickmark == -1)) {
-                //additional forward in buffer to look for release marked note
-                // tickmark is -1 only if removeNote called after release
-                l1++;
+            // note is not on top: take out the note and pull down all above
+            index = 0;
+            while ((index < noteCount) && (note > notes[bufPtr][0][index])) index++;
+
+            // additional forward in buffer to look for release marked note
+            // tick is -1 only if removeNote called after release
+            while ((index < noteCount) && (!notes[bufPtr][3][index])
+                    && (tick == -1)) {
+                index++;
             }
-            
-            if (note == notes[bufPtr][0][l1]) {
-                for (l3 = 0; l3 < 4; l3++) {
-                    for (l2 = l1; l2 < noteCount - 1; l2++) {
-                        notes[bufPtr][l3][l2] = notes[bufPtr][l3][l2 + 1];
-                    }  
-                }  
-                for (l2 = l1; l2 < noteCount - 1; l2++) {
+
+            if (note == notes[bufPtr][0][index]) {
+                deleteNoteAt(index, bufPtr);
+                for (int l2 = index; l2 < noteCount; l2++) {
                     old_attackfn[l2] = old_attackfn[l2 + 1];
-                } 
-                noteCount--;
+                }
             }
         }
-    } 
-    else {
-        //mark as released but keep with note off time tick
-        l1 = 0;
-        while ((l1 < noteCount) && (note > notes[bufPtr][0][l1])) l1++;
-        while ((l1 < noteCount) && (notes[bufPtr][3][l1])) l1++;
-        if (note == notes[bufPtr][0][l1]) {
-            notes[bufPtr][3][l1] = 1;
-            notes[bufPtr][2][l1] = tickmark;
+    }
+    else tagAsReleased(note, tick, bufPtr);
+
+    copyNoteBuffer();
+}
+
+void MidiArp::deleteNoteAt(int index, int bufPtr)
+{
+    for (int l3 = 0; l3 < 4; l3++) {
+        for (int l2 = index; l2 < noteCount - 1; l2++) {
+            notes[bufPtr][l3][l2] = notes[bufPtr][l3][l2 + 1];
         }
     }
-    newBufPtr = noteBufPtr;
-    noteBufPtr = bufPtr;
-    for (l3 = 0; l3 < 4; l3++) {
-        for (l2 = 0; l2 < noteCount; l2++) {
-            notes[newBufPtr][l3][l2] = notes[bufPtr][l3][l2];
-        }  
+    noteCount--;
+}
+
+void MidiArp::tagAsReleased(int note, int tick, int bufPtr)
+{
+    //mark as released but keep with note off time tick
+    int l1 = 0;
+    while ((l1 < noteCount) && (note > notes[bufPtr][0][l1])) l1++;
+    while ((l1 < noteCount) && (notes[bufPtr][3][l1])) l1++;
+    if (note == notes[bufPtr][0][l1]) {
+        notes[bufPtr][3][l1] = 1;
+        notes[bufPtr][2][l1] = tick;
+    }
+}
+
+void MidiArp::copyNoteBuffer()
+{
+    int newBufPtr = noteBufPtr;
+    noteBufPtr++;
+    noteBufPtr%=2;
+
+    for (int l3 = 0; l3 < 4; l3++) {
+        for (int l2 = 0; l2 < noteCount; l2++) {
+            notes[newBufPtr][l3][l2] = notes[noteBufPtr][l3][l2];
+        }
     }
 }
 
 void MidiArp::getNote(int *tick, int note[],
         int velocity[], int *length)
-{ 
+{
     QChar c;
     int l1, tmpIndex[MAXCHORD], chordIndex, grooveTmp;
     bool gotCC, outOfRange, pause;
     double attackfn, releasefn;
-    
+
 
     chordIndex = 0;
     tmpIndex[0] = 0;
     tmpIndex[1] = -1;
     gotCC = false;
     pause = false;
-    
+
 
     if (!patternIndex) initLoop();
-    do {  
-        if (patternLen) 
-            c = (pattern.at(patternIndex)); 
-        else 
+    do {
+        if (patternLen)
+            c = (pattern.at(patternIndex));
+        else
             c = ' ';
-            
+
         if (c != ' ') {
             if (c.isDigit() || (c == 'p')) {
                 tmpIndex[chordIndex] = c.digitValue() + noteOfs;
@@ -293,11 +290,11 @@ void MidiArp::getNote(int *tick, int note[],
                 }
                 gotCC = false;
                 pause = (c == 'p');
-            } 
+            }
             else {
                 gotCC = true;
 
-                switch(c.toAscii()) { 
+                switch(c.toAscii()) {
                     case '(':
                         chordMode = true;
                         break;
@@ -317,13 +314,13 @@ void MidiArp::getNote(int *tick, int note[],
                         octave=0;
                         break;
                     case '>':
-                        tempo /= 2.0;
+                        stepWidth /= 2.0;
                         break;
                     case '<':
-                        tempo *= 2.0;
+                        stepWidth *= 2.0;
                         break;
                     case '.':
-                        tempo = 1.0;
+                        stepWidth = 1.0;
                         break;
                     case '/':
                         vel += 0.2;
@@ -341,67 +338,67 @@ void MidiArp::getNote(int *tick, int note[],
             }
         }
     } while (advancePatternIndex(false) && (gotCC || chordMode));
-      
+
     l1 = 0;
-    
+
     if (noteCount) do {
-        noteIndex[l1] = (noteCount) ? tmpIndex[l1] % noteCount : 0; 
+        noteIndex[l1] = (noteCount) ? tmpIndex[l1] % noteCount : 0;
         note[l1] = clip(notes[noteBufPtr][0][noteIndex[l1]]
                 + octave * 12, 0, 127, &outOfRange);
         grooveTmp = (grooveIndex % 2) ? -grooveVelocity : grooveVelocity;
-        
+
         if ((release_time > 0) && (notes[noteBufPtr][3][noteIndex[l1]])) {
-            releasefn = 1.0 - (double)(arpTick 
+            releasefn = 1.0 - (double)(arpTick
                     - notes[noteBufPtr][2][noteIndex[l1]])
-                    / release_time / (double)TICKS_PER_QUARTER 
+                    / release_time / (double)TPQN
                     * 60 / queueTempo;
-                    
+
             if (releasefn < 0.0) releasefn = 0.0;
-        } 
+        }
         else releasefn = 1.0;
 
         if (attack_time > 0) {
             if (!notes[noteBufPtr][3][noteIndex[l1]]) {
-                attackfn = (double)(arpTick 
+                attackfn = (double)(arpTick
                     - notes[noteBufPtr][2][noteIndex[l1]])
-                    / attack_time / (double)TICKS_PER_QUARTER 
+                    / attack_time / (double)TPQN
                     * 60 / queueTempo;
-                    
+
                 if (attackfn > 1.0) attackfn = 1.0;
                 old_attackfn[noteIndex[l1]] = attackfn;
-            } 
+            }
             else attackfn = old_attackfn[noteIndex[l1]];
-        } 
+        }
         else attackfn = 1.0;
-        
+
         velocity[l1] = clip((double)notes[noteBufPtr][1][noteIndex[l1]]
                 * vel * (1.0 + 0.005 * (double)(randomVelocity + grooveTmp))
                 * releasefn * attackfn, 0, 127, &outOfRange);
-                
+
         if ((notes[noteBufPtr][3][noteIndex[l1]]) && (!velocity[l1]))
-        
+
             removeNote(&notes[noteBufPtr][0][noteIndex[l1]], -1, 0);
 
         else
 
             l1++;
-            
+
     } while ((tmpIndex[l1] >= 0) && (l1 < MAXCHORD - 1) && (l1 < noteCount));
 
     note[l1] = -1; // mark end of array
     grooveTmp = (grooveIndex % 2) ? -grooveLength : grooveLength;
-    *length = clip(len * tempo * (double)TICKS_PER_QUARTER
+    *length = clip(len * stepWidth * (double)TPQN
             * (1.0 + 0.005 * (double)(randomLength + grooveTmp)), 2,
             1000000,  &outOfRange);
     grooveTmp = (grooveIndex % 2) ? -grooveTick : grooveTick;
-    arpTick += tempo * (double)TICKS_PER_QUARTER
+    arpTick += stepWidth * (double)TPQN
         * (1.0 + 0.005 * (double)grooveTmp);
-    *tick = arpTick + clip(tempo * 0.25 * (double)randomTick, 0,
+    *tick = arpTick + clip(stepWidth * 0.25 * (double)randomTick, 0,
             1000, &outOfRange);
-    
+
     if (!(patternLen && noteCount) || pause || isMuted) {
         velocity[0] = 0;
-    }  
+    }
     grooveIndex++;
     emit nextStep(grooveIndex);
 }
@@ -422,12 +419,12 @@ bool MidiArp::advancePatternIndex(bool reset)
                 break;
             case 2:
                 noteOfs--;
-                if ((noteCount -1 < patternMaxIndex) || 
+                if ((noteCount -1 < patternMaxIndex) ||
                     (noteOfs < patternMaxIndex)) {
                     noteOfs = noteCount - 1;
                 }
                 break;
-            default:  
+            default:
                 noteOfs = 0;
         }
         return(false);
@@ -437,25 +434,25 @@ bool MidiArp::advancePatternIndex(bool reset)
 
 void MidiArp::initLoop()
 {
-    tempo = 1.0;
-    len = 0.5;  
-    vel = 0.8;  
-    octave = 0; 
+    stepWidth = 1.0;
+    len = 0.5;
+    vel = 0.8;
+    octave = 0;
     grooveIndex = 0;
 }
 
-void MidiArp::getNextNote(int askedTick)
-{ 
+void MidiArp::prepareNextNote(int askedTick)
+{
     int l1 = 0;
     returnNote.clear();
     returnVelocity.clear();
 
     nextNoteTick = askedTick;
-    while ((nextNote[l1] >= 0) && (l1 < MAXCHORD - 1)) { 
+    while ((nextNote[l1] >= 0) && (l1 < MAXCHORD - 1)) {
         returnNote.append(nextNote[l1]);
         returnVelocity.append(nextVelocity[l1]);
         l1++;
-    }  
+    }
     returnNote.append(-1); // mark end of chord
     returnLength = nextLength;
     returnIsNew = newNext;
@@ -469,11 +466,11 @@ int MidiArp::getNextNoteTick()
 
 bool MidiArp::wantTrigByKbd()
 {
-	bool on = (getPressedNoteCount() && trigByKbd);
+    bool on = ((getPressedNoteCount() == 1) && trigByKbd);
     return(on);
 }
 
-void MidiArp::getCurrentNote(int askedTick)
+void MidiArp::prepareCurrentNote(int askedTick)
 {
     currentTick = askedTick;
     start(Priority(6));
@@ -488,12 +485,12 @@ void MidiArp::run()
     returnTick = currentNoteTick;
     returnNote.clear();
     returnVelocity.clear();
-    
-    while ((currentNote[l1] >= 0) && (l1 < MAXCHORD - 1)) { 
+
+    while ((currentNote[l1] >= 0) && (l1 < MAXCHORD - 1)) {
         returnNote.append(currentNote[l1]);
         returnVelocity.append(currentVelocity[l1]);
         l1++;
-    }  
+    }
     returnNote.append(-1); // mark end of chord
     returnLength = currentLength;
     returnIsNew = newCurrent;
@@ -504,42 +501,35 @@ void MidiArp::run()
 void MidiArp::updateNotes(int currentTick)
 {
     int l1 = 0;
-    
+
     //allow 8 ticks of tolerance for echo tick for external sync
     if ((currentTick + 8) >= currentNoteTick) {
         currentNoteTick = nextNoteTick;
         getNote(&nextNoteTick, nextNote, nextVelocity, &nextLength);
-        while ((nextNote[l1] >= 0) && (l1 < MAXCHORD - 1)) { 
+        while ((nextNote[l1] >= 0) && (l1 < MAXCHORD - 1)) {
             currentNote[l1] = nextNote[l1];
             currentVelocity[l1] = nextVelocity[l1];
             l1++;
-        }  
+        }
         currentNote[l1] = -1; // mark end of chord
         currentLength = nextLength;
         newCurrent = true;
         newNext = true;
-    } 
+    }
 }
 
 void MidiArp::foldReleaseTicks(int currentTick)
 {
-    int bufPtr, newBufPtr, l2, l3;
-    
+    int bufPtr, l2;
+
     mutex.lock();
     bufPtr = (noteBufPtr) ? 0 : 1;
-    
+
     for (l2 = 0; l2 < noteCount; l2++) {
             notes[bufPtr][2][l2] -= currentTick;
     }
-    
-    newBufPtr = noteBufPtr;
-    noteBufPtr = bufPtr;
-    
-    for (l3 = 0; l3 < 4; l3++) {
-        for (l2 = 0; l2 < noteCount; l2++) {
-            notes[newBufPtr][l3][l2] = notes[bufPtr][l3][l2];
-        }  
-    }
+
+    copyNoteBuffer();
     mutex.unlock();
 }
 
@@ -566,16 +556,16 @@ void MidiArp::updatePattern(const QString& p_pattern)
     patternMaxIndex = 0;
     if (patternLen)
     {
-        c = (pattern.at(patternLen - 1)); 
+        c = (pattern.at(patternLen - 1));
         while (!c.isDigit() && (c != 'p') && (c != ')'))
         {
             pattern = pattern.left(patternLen - 1);
             patternLen--;
             if (patternLen < 1) break;
-            c = (pattern.at(patternLen - 1)); 
+            c = (pattern.at(patternLen - 1));
         }
     }
-    
+
     for (l1 = 0; l1 < patternLen; l1++) {
         c = pattern.at(l1);
         if (c.isDigit() && (c.digitValue() > patternMaxIndex)) {
@@ -599,7 +589,7 @@ int MidiArp::clip(int value, int min, int max, bool *outOfRange)
     } else if (tmp < min) {
         tmp = min;
         *outOfRange = true;
-    }  
+    }
     return(tmp);
 }
 
@@ -648,19 +638,19 @@ void MidiArp::updateQueueTempo(int val)
 void MidiArp::updateTriggerMode(int val)
 {
     switch (val) {
-        case 0: 
+        case 0:
             trigByKbd = false;
             restartByKbd = false;
         break;
-        case 1: 
+        case 1:
             trigByKbd = false;
             restartByKbd = true;
         break;
-        case 2: 
+        case 2:
             trigByKbd = true;
             restartByKbd = true;
         break;
-        default:  
+        default:
             trigByKbd = false;
             restartByKbd = false;
     }
@@ -692,7 +682,7 @@ void MidiArp::purgeSustainBuffer(int sustick)
     for (int l1 = 0; l1 < sustainBuffer.count(); l1++) {
         int buf = sustainBuffer.at(l1);
         removeNote(&buf, sustick, 1);
-    }  
+    }
     sustainBuffer.clear();
 }
 
@@ -707,7 +697,7 @@ void MidiArp::purgeLatchBuffer()
     for (int l1 = 0; l1 < latchBuffer.count(); l1++) {
         int buf = latchBuffer.at(l1);
         removeNote(&buf, arpTick, 1);
-    }  
+    }
     latchBuffer.clear();
 }
 
diff --git a/src/midiarp.h b/src/midiarp.h
index 76bd389..fc27766 100644
--- a/src/midiarp.h
+++ b/src/midiarp.h
@@ -1,3 +1,27 @@
+/*!
+ * @file midiarp.h
+ * @brief Member definitions for the MidiArp MIDI worker class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef MIDIARP_H
 #define MIDIARP_H
 
@@ -7,16 +31,40 @@
 #include <QThread>
 #include <QTimer>
 #include <QVector>
-#include <alsa/asoundlib.h>
 #include <main.h>
 
-
+ /*!
+ * @brief MIDI worker class for the Arpeggiator Module. Implements the
+ * functions providing note arpeggiation as a QThread.
+ *
+ * The parameters of MidiArp are controlled by the ArpWidget class.
+ * A pointer to MidiArp is passed to the SeqDriver thread, which calls
+ * the MidiArp::prepareCurrentNote member as a function of the position of
+ * the ALSA queue. MidiArp will then call its
+ * internal MidiArp::getNote function which produces an array of notes
+ * stored in its internal output buffer. The notes in the array depend
+ * on the active MidiArp::pattern, envelope, random and groove settings.
+ * The note events consist of timing information
+ * (tick and length), note values and velocity values. MidiArp::getNote
+ * also advances the pattern index and emits the MidiArp::nextStep signal
+ * to update the cursor position in the graphical ArpScreen display part
+ * of ArpWidget. SeqDriver then
+ * accesses this output buffer and sends it to the ALSA queue. SeqDriver
+ * also calls MidiArp::handleNoteOn and MidiArp::handleNoteOff. These members
+ * manage the arpeggiator input note buffer
+ * also part of this class. Notes received on the ALSA input port will
+ * therefore be added or removed from the buffer as SeqDriver transfers
+ * them to this class.
+ */
 class MidiArp : public QThread  {
-    
+
   Q_OBJECT
-    
+
   private:
-    int nextNote[MAXCHORD], nextVelocity[MAXCHORD];
+    int nextNote[MAXCHORD]; /*!< Holds the note values to be output next
+                                @see MidiArp::updateNotes */
+    int nextVelocity[MAXCHORD]; /*!< Holds the associated velocities to be output next
+                                    @see MidiArp::updateNotes, MidiArp::nextNote */
     int currentNoteTick, nextNoteTick, currentTick, arpTick;
     int currentNote[MAXCHORD], currentVelocity[MAXCHORD];
     int currentLength, nextLength;
@@ -24,77 +72,384 @@ class MidiArp : public QThread  {
     int grooveTick, grooveVelocity, grooveLength, grooveIndex;
     int randomTick, randomVelocity, randomLength;
     double queueTempo;
+    double stepWidth, len, vel;
 
-    QVector<int> sustainBuffer;
-    QVector<int> latchBuffer;
-    QTimer *latchTimer;
+    QVector<int> sustainBuffer; /*!< Holds released note values when MidiArp::sustain is True */
+    QVector<int> latchBuffer;   /*!< Holds released note values when MidiArp::latch_mode is True */
+    QTimer *latchTimer;         /*!< Is started when a note is released and causes MidiArp::purgeLatchBuffer after
+                                    a delay of 200ms except when another released note is detected before. */
 
     bool sustain, latch_mode;
     int octave, noteIndex[MAXCHORD], patternIndex;
-    int notes[2][4][MAXNOTES]; // Buffer Index, Note/Velocity/On-offTick/releaseMark, Data Index
+ /*! @brief The input note buffer array of the Arpeggiator, which has
+  * two array copies.
+  *
+  * @par The first index (0:1) selects the copy to be modified
+  * avoiding access conflicts while modifying its data.
+  * @par The second index selects the columns corresponding to
+  * - 0: Note value
+  * - 1: Note velocity
+  * - 2: Timing of the note event in internal ticks (NOTE_ON or NOTE_OFF)
+  * - 3: Release tag (0: note is kept in buffer, 1: MidiArp::getNote
+  * will decrease this note's velocity until it reaches 0, and then
+  * definitely remove the by a MidiArp::removeNote call.
+  *
+  * @par The third index is the note index in the buffer.
+  * */
+    int notes[2][4][MAXNOTES];
+
+ /*! @brief The storage copy of dynamic attack values.
+  *
+  * These values are to be multiplied with the
+  * velocity at each new arpeggiator step. Its index corresponds
+  * to that of the third MidiArp::notes buffer index.
+  * */
     double old_attackfn[MAXNOTES];
-    int noteBufPtr, noteCount, patternLen, patternMaxIndex, noteOfs;
+    int noteBufPtr;     /*!< Pointer to the currently active note buffer copy */
+    int noteCount;      /*!< The number of notes in the MidiArp::notes buffer */
+    int patternLen;     /*!< Length of the arp text pattern */
+    int patternMaxIndex;/*!< Maximum number of stacked notes in the pattern */
+    int noteOfs;        /*!< The current index in a chord. @see repeatPatternThroughChord */
 
     QMutex mutex;
-    void initLoop();  
+
+
+/**
+ * @brief This function resets all attributes the pattern
+ * accumulates during run.
+ *
+ * It is called when the currentIndex revolves to restart the loop with
+ * default velocity, step width, octave and length.
+*/
+    void initLoop();
+/**
+ * @brief This function allows forcing an integer value within the
+ * specified range (clip).
+ *
+ * @param value The value to be checked
+ * @param min The minimum allowed return value
+ * @param max The maximum allowed return value
+ * @param outOfRange Is set to True if value was outside min|max range
+ * @return The value clipped within the range
+ */
     int clip(int value, int min, int max, bool *outOfRange);
-    void updateNotes(int currentTick);  
+/**
+ * @brief This function updates the current note arrays with new values
+ * obtained from MidiArp::getNote
+ *
+ * It is called by MidiArp::run and calls MidiArp::getNote if the given
+ * timing is ahead of the last timing information. It then transfers the
+ * MidiArp::nextNote and MidiArp::nextVelocity arrays into
+ * MidiArp::currentNote and MidiArp::currentVelocity.
+ *
+ * @param currentTick The current timing information in internal ticks
+ */
+    void updateNotes(int currentTick);
+/**
+ * @brief This is MidiArp's main note processor producing output notes
+ * from input notes.
+ *
+ * It analyzes the MidiArp::pattern text and MidiArp::notes input buffer
+ * to yield arrays of notes that have to be output at the given timing.
+ * Only in case of an arpeggio step involving chords, these arrays have
+ * sizes > 1.
+ * @param tick The timing of the requested arpeggio step
+ * @param note The array of notes to be filled
+ * @param velocity The associated array of velocites to be filled
+ * @param length The note length for this arpeggio step
+ */
     void getNote(int *tick, int note[], int velocity[], int *length);
+/**
+ * @brief This function is currently not in use.
+ *
+ * It transfers the content of the MidiArp::nextNote array to
+ * MidiArp::returnNote, MidiArp::returnVelocity and MidiArp::returnTick.
+ *
+ * @param askedTick The timing value in ticks, for which the next note
+ * information is queried.
+ */
+    void prepareNextNote(int askedTick);
+/**
+ * @brief This function returns the number of notes present at the ALSA
+ * input port.
+ *
+ * This is the number of notes currently pressed on the keyboard. Note
+ * that the input MidiArp::notes buffer size can be different from this
+ * number, since it can contain notes in release state or in the
+ * MidiArp::latchBuffer.
+ *
+ * @return Number of notes present at the ALSA input port.
+ */
+    int getPressedNoteCount();
+/**
+ * @brief This function returns the number of notes present at the ALSA
+ * input port.
+ *
+ * This is the number of notes currently pressed on the keyboard. Note
+ * that the input MidiArp::notes buffer size can be different from this
+ * number, since it can contain notes in release state or in the
+ * MidiArp::latchBuffer.
+ *
+ * @return Number of notes present at the ALSA input port.
+ */
+    void removeNote(int *noteptr, int tick, int keep_rel);
+/**
+ * @brief This function calculates the index of the next arpeggio
+ * step and revolves it if necessary.
+ *
+ * The next step depends on the MidiArp::repeatPatternThrough chord mode.
+ * The pattern index can be simply reset to zero if the reset flag is
+ * set to True.
+ * @param reset Set to True in order to set the pattern index to zero
+ * @return True if the pattern index is now zero
+ */
     bool advancePatternIndex(bool reset);
-   
+/**
+ * @brief This function removes a note inside the MidiArp::notes input
+ * note buffer.
+ *
+ * The note  at the given index is deleted from the buffer with the
+ * given bufPtr index (0 or 1), and notes at higher positions in the
+ * buffer are moved in position.
+ * @param index Index of the note to delete from the buffer
+ * @param bufPtr Buffer copy to work with
+ */
+    void deleteNoteAt(int index, int bufPtr);
+/**
+ * @brief This function sets the released flag (value 1) for the note
+ * at the given index
+ *
+ * This operation is done for the buffer with given bufPtr index (0 or 1).
+ * A released flag set will cause getNote to diminish the velocity of
+ * this note at each arpeggio step, and to remove it when the velocity
+ * reaches zero.
+ *
+ * @param note Note value to be tagged as released
+ * @param tick The time in internal ticks at which the note was released
+ * @param bufPtr The index of the MidiArp::notes buffer currently in use
+ */
+    void tagAsReleased(int note, int tick, int bufPtr);
+/**
+ * @brief This function performs a copy within MidiArp::notes from the
+ * currently active index to the inactive index
+ */
+    void copyNoteBuffer();
+
   public:
-    int chIn;       // Channel of input events
-    int indexIn[2]; // Index input/output (for Controller events)
-    int rangeIn[2]; // Parameter that is mapped, [0] low, [1] high boundary
-    int portOut;    // Output port (ALSA Sequencer)
-    int channelOut;
-    bool hold, isMuted;
-    bool restartByKbd, trigByKbd;
-    int repeatPatternThroughChord;
-    double tempo, len, vel;
-    double attack_time, release_time; 
-    int randomTickAmp, randomVelocityAmp, randomLengthAmp;
-    QString pattern;
-    QVector<int> returnNote, returnVelocity;
-    int returnTick, returnIsNew, returnLength;
-           
+    int chIn;       /*!< Input channel state set by ArpWidget */
+    int indexIn[2]; /*!< Note range filter 0: lower, 1: upper limit, set by ArpWidget */
+    int rangeIn[2]; /*!< Velocity range filter 0: lower, 1: upper limit, set by ArpWidget */
+    int portOut;    /*!< Output port, set by ArpWidget */
+    int channelOut; /*!< Output channel, set by ArpWidget */
+    bool isMuted;   /*!< Mute state set by ArpWidget */
+    bool restartByKbd; /*!< If True, restart pattern at 0 upon new received note, set by ArpWidget */
+    bool trigByKbd; /*!< If True, trigger current note tick by tick of received note, set by ArpWidget */
+    int repeatPatternThroughChord; /*!< Repeat mode "Static", "Up", "Down", set by ArpWidget */
+    double attack_time;/*!< Attack time in seconds, set by ArpWidget */
+    double release_time;/*!< Release time in seconds, set by ArpWidget */
+    int randomTickAmp; /*!< Amplitude of timing randomization, set by ArpWidget */
+    int randomVelocityAmp; /*!< Amplitude of velocity randomization, set by ArpWidget */
+    int randomLengthAmp; /*!< Amplitude of length randomization, set by ArpWidget */
+    QString pattern; /*!< Holds the the arpeggio pattern text */
+
+    QVector<int> returnNote; /*!< Holds the notes of the currently active arpeggio step */
+    QVector<int> returnVelocity; /*!< Holds the velocities of the currently active arpeggio step */
+    int returnTick; /*!< Holds the time in internal ticks of the currently active arpeggio step */
+    int returnLength; /*!< Holds the note length of the currently active arpeggio step */
+    int returnIsNew;
+
   public:
     MidiArp();
     ~MidiArp();
-    bool isArp(snd_seq_event_t *evIn);   // Check if evIn is in the input range of the arp
-    void addNote(int note, int velocity, int tick); // Add input Note for Arpeggio
-    void handleNoteOff(int note, int tick, int keep_rel); // Remove input Note from Arpeggio
-    void removeNote(int *noteptr, int tick, int keep_rel); // Remove input Note from Arpeggio
-    void getCurrentNote(int askedTick);
-    void getNextNote(int askedTick);
-    int getNextNoteTick();
-    int getPressedNoteCount();
+    void updatePattern(const QString&);
+    void updateTriggerMode(int val);
+    void updateRandomTickAmp(int);
+    void updateRandomVelocityAmp(int);
+    void updateRandomLengthAmp(int);
+    void updateAttackTime(int);
+    void updateQueueTempo(int);
+    void updateReleaseTime(int);
+/*! @brief This function sets MidiArp::isMuted, which is checked by
+ * SeqDriver and which suppresses data output globally if set to True.
+ *
+ * @param on Set to True to suppress data output to ALSA
+ */
+    void setMuted(bool);
+/**
+ * @brief This function checks whether an ALSA event is eligible for this
+ * module.
+ *
+ * Its response depends on the input filter settings, i.e. note range,
+ * velocity range and channel.
+ *
+ * @param inEv MidiEvent event to check
+ * @return True if inEv is in the input range of the module
+ */
+    bool wantEvent(MidiEvent event);
+/**
+ * @brief This function checks whether this module is set to keyboard
+ * trigger mode.
+ *
+ * Its response depends on MidiArp::trigByKbd and whether there are notes
+ * pressed on the keyboard, i.e. whether the note was played stakato.
+ *
+ * @return True if the module accepts to be triggered
+ */
     bool wantTrigByKbd();
+/**
+ * @brief This function does the actions related to a newly received note.
+ *
+ * It is called by SeqDriver when a new note is received on the ALSA input port.
+ * The MidiArp::latchBuffer is purged if the note was played stakato.
+ * Depending on the trigger settings, the Arp's timing is reset to
+ * that of the note tick and/or the pattern index is reset. The note with
+ * its attributes will then be inserted in the MidiArp::notes buffer, which
+ * is sorted in accending note value order, and MidiArp::copyNoteBuffer is
+ * called.
+ *
+ * @param note The note value of the received note
+ * @param velocity The note velocity
+ * @param tick The time the note was received in internal ticks
+ */
+    void handleNoteOn(int note, int velocity, int tick);
+/**
+ * @brief This function does the actions related to a note release detected
+ * on the ALSA input port.
+ *
+ * It is called by SeqDriver when a NOTE_OFF event is received. The function
+ * will go through checks regarding MidiArp::latchMode and MidiArp::sustain
+ * and add the note to the respective MidiArp::latchBuffer and/or
+ * MidiArp::sustainBuffer if required. If not, the note is either tagged
+ * as released (provided MidiArp::release_time is set) or removed from
+ * the buffer. The latter depends on the keep_rel argument.
+ *
+ * @param note The note value of the received note
+ * @param tick The time the note was released in internal ticks
+ * @param keep_rel Set this flag to 1 if the note is to be kept in the buffer
+ * along with the release tick and tagged as a released note. 0 otherwise for
+ * definite removal from the buffer.
+ */
+    void handleNoteOff(int note, int tick, int keep_rel);
+/**
+ * @brief This function represents the external interface to the
+ * core of the arpeggiator engine.
+ *
+ * It is called by SeqDriver when a previously scheduled SND_SEQ_ECHO
+ * event is received on the ALSA port.
+ * It starts the MidiArp::run thread. In the thread, MidiArp::getNote
+ * is called, which does the note processing depending on the
+ * MidiArp::pattern and
+ * leading to new data in MidiArp::returnNote, MidiArp::returnVelocity,
+ * MidiArp::returnLength. SeqDriver then accesses this data directly
+ * and outputs it to the ALSA queue.
+ *
+ * @param askedTick the timing of the note(s) to be output
+ *
+ */
+    void prepareCurrentNote(int askedTick);
+/**
+ * @brief This function returns the timing of the next note to be
+ * calculated and played out in internal ticks.
+ *
+ * It is called by SeqDriver immediately after accessing the current note
+ * data. This next note timing information is used to
+ * schedule a so called echo event which will trigger the next call to
+ * prepareCurrentNote followed by an output of note data to the ALSA
+ * queue.
+ *
+ * @return The timing of the next coming note in internal ticks.
+ */
+    int getNextNoteTick();
+/**
+ * @brief This function resets the pattern index and sets the current
+ * timing of the arpeggio to currentTick.
+ *
+ * It is called by SeqDriver when the ALSA queue is started, or when
+ * a stakato note is received while MidiArp::restartByKbd is set.
+ *
+ * @param currentTick The timing in internal ticks, relative to which
+ * the following arpeggio notes are calculated.
+ */
     void initArpTick(int currentTick);
+/**
+ * @brief This function ensures continuity of the release function
+ * when the currentTick position jumps into
+ * the past.
+ *
+ * It should be called whenever the transport position is looping. At
+ * this time, this is the case when JACK Transport is looping.
+
+ * @param currentTick The current time position in internal ticks.
+ */
     void foldReleaseTicks(int currentTick);
+/**
+ * @brief This function seeds new random values for the three parameters
+ * concerned, timing (tick), velocity and length.
+ *
+ * This function is
+ * called by MidiArp::getNote at every new note. It uses the
+ * MidiArp::randomTickAmp, MidiArp::randomVelocityAmp and
+ * MidiArp::randomLengthAmp settings coming from ArpWidget.
+ * The values are then used by MidiArp::getNote.
+ */
     void newRandomValues();
+/**
+ * @brief This function copies the new values transferred from the
+ * GrooveWidget into variables used by MidiArp::getNote.
+ *
+ * @param p_grooveTick Groove amount for timing displacements
+ * @param p_grooveVelocity Groove amount for velocity variations
+ * @param p_grooveLength Groove amount for note length variations
+ */
     void newGrooveValues(int p_grooveTick, int p_grooveVelocity,
             int p_grooveLength);
+ /*! @brief Set by SeqDriver when MidiCC #64 is received.
+  *
+  * Will cause notes remaining in MidiArp::sustainBuffer until
+  * set to false.
+  * @param sustain Set to True to cause hold mode
+  * @param tick Time in internal ticks at which the controller was received */
+    void setSustain(bool sustain, int tick);
+ /*! @brief Will cause notes remaining in MidiArp::latchBuffer until new
+  * stakato note received
+  *
+  * This function is called by ArpWidget::setLatchMode
+  */
+    void setLatchMode(bool);
+ /*! @brief Calls MidiArp::removeNote for all notes in MidiArp::sustainBuffer
+  * and then clears sustainBuffer.
+  *
+  * This function is called by MidiArp::setSustain
+  * @param sustick Time in internal ticks at which the controller was received */
+    void purgeSustainBuffer(int sustick);
+ /*! @brief sets MidiArp::noteCount to zero and clears MidiArp::latchBuffer. */
+    void clearNoteBuffer();
+/**
+ * @brief This is the thread main function, which calls
+ * MidiArp::updateNotes().
+ *
+ * It then copies the newly calculated MidiArp::currentNote
+ * and MidiArp::currentVelocity arrays into the return
+ * members, which can then be accessed
+ * by the SeqDriver::run thread.
+ */
     void run();
 
   signals:
+/**
+ * @brief Emitted to ArpScreen::update at every arpeggio step.
+ *
+ * It causes update of the cursor position and pattern display.
+ * @param patternIndex The current index position in the MidiArp::pattern.
+ */
     void nextStep(int patternIndex);
 
-  public slots:  
-    void updatePattern(const QString&);
-    void updateTriggerMode(int val);
-    void updateRandomTickAmp(int);
-    void updateRandomVelocityAmp(int);
-    void updateRandomLengthAmp(int);
-    void updateAttackTime(int);
-    void updateQueueTempo(int);
-    void updateReleaseTime(int);
-    void setMuted(bool); //set mute
-    void setSustain(bool, int); //set sustain
-    void setLatchMode(bool); //set latch mode
-    void purgeSustainBuffer(int sustick);
+  public slots:
+ /*! @brief Slot for MidiArp::latchTimer. Calls MidiArp::removeNote for
+  * all notes in MidiArp::latchBuffer and then clears latchBuffer.
+  */
     void purgeLatchBuffer();
-    void clearNoteBuffer();
+
 };
-                              
+
 #endif
diff --git a/src/midicctable.cpp b/src/midicctable.cpp
index e224840..dcc0f02 100644
--- a/src/midicctable.cpp
+++ b/src/midicctable.cpp
@@ -1,22 +1,26 @@
-/*
- *      midicctable.cpp
- *      
+/*!
+ * @file midicctable.cpp
+ * @brief Implementation of the MidiCCTable QDialog class
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
 
@@ -25,20 +29,20 @@
 #include "midicctable.h"
 #include "mainwindow.h"
 
-MidiCCTable::MidiCCTable(ArpData *p_arpData, QWidget *parent) : QDialog(parent)
+MidiCCTable::MidiCCTable(Engine *p_engine, QWidget *parent) : QDialog(parent)
 {
-    arpData = p_arpData;
+    engine = p_engine;
     QGridLayout *boxLayout = new QGridLayout(this);
     midiCCTable = new QTableWidget(this);
-    
+
     midiCCTable->clear();
-    midiCCTable->setRowCount(arpData->moduleWindowCount()*10);
+    midiCCTable->setRowCount(engine->moduleWindowCount()*10);
     midiCCTable->setColumnCount(7);
     midiCCTable->setColumnHidden(5, true);
     midiCCTable->setColumnHidden(6, true);
     connect(midiCCTable, SIGNAL(itemChanged(QTableWidgetItem*)),
-                    this, SLOT(itemChanged(QTableWidgetItem*)));    
-    
+                    this, SLOT(itemChanged(QTableWidgetItem*)));
+
     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
                                     | QDialogButtonBox::Cancel);
     removeButton = new QPushButton(tr("Re&move"), this);
@@ -46,13 +50,13 @@ MidiCCTable::MidiCCTable(ArpData *p_arpData, QWidget *parent) : QDialog(parent)
     buttonBox->addButton(removeButton, QDialogButtonBox::ActionRole);
     buttonBox->addButton(revertButton, QDialogButtonBox::ResetRole);
     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
-    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));    
-    connect(removeButton, SIGNAL(clicked()), this, SLOT(removeCurrent()));    
-    connect(revertButton, SIGNAL(clicked()), this, SLOT(revert()));    
-    
+    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+    connect(removeButton, SIGNAL(clicked()), this, SLOT(removeCurrent()));
+    connect(revertButton, SIGNAL(clicked()), this, SLOT(revert()));
+
     boxLayout->addWidget(midiCCTable, 0, 0);
     boxLayout->addWidget(buttonBox, 1, 0);
-    
+
     setLayout(boxLayout);
     setModal(true);
     setWindowTitle(tr("MIDI Controllers - ") + APP_NAME);
@@ -69,42 +73,42 @@ void MidiCCTable::getCurrentControls()
     int nrows = 0;
 
     midiCCTable->clear();
-    
-    for (l1 = 0; l1 < arpData->arpWidgetCount(); l1++) {
-        ccList = arpData->arpWidget(l1)->ccList;
-        
-        for (l2 = 0; l2 < arpData->arpWidget(l1)->ccList.count(); l2++) {
-        
+
+    for (l1 = 0; l1 < engine->arpWidgetCount(); l1++) {
+        ccList = engine->arpWidget(l1)->ccList;
+
+        for (l2 = 0; l2 < engine->arpWidget(l1)->ccList.count(); l2++) {
+
             midiCCTable->setVerticalHeaderItem(nrows,
-                    new QTableWidgetItem(arpData->arpWidget(l1)->name));
-                    
-            fillControlRow(nrows, ccList.at(l2), arpData->arpWidget(l1)->ID);
+                    new QTableWidgetItem(engine->arpWidget(l1)->name));
+
+            fillControlRow(nrows, ccList.at(l2), engine->arpWidget(l1)->ID);
             nrows++;
         }
     }
-    
-    for (l1 = 0; l1 < arpData->lfoWidgetCount(); l1++) {
-        ccList = arpData->lfoWidget(l1)->ccList;
-        
-        for (l2 = 0; l2 < arpData->lfoWidget(l1)->ccList.count(); l2++) {
-        
+
+    for (l1 = 0; l1 < engine->lfoWidgetCount(); l1++) {
+        ccList = engine->lfoWidget(l1)->ccList;
+
+        for (l2 = 0; l2 < engine->lfoWidget(l1)->ccList.count(); l2++) {
+
             midiCCTable->setVerticalHeaderItem(nrows,
-                    new QTableWidgetItem(arpData->lfoWidget(l1)->name));
-                    
-            fillControlRow(nrows, ccList.at(l2), arpData->lfoWidget(l1)->ID);
+                    new QTableWidgetItem(engine->lfoWidget(l1)->name));
+
+            fillControlRow(nrows, ccList.at(l2), engine->lfoWidget(l1)->ID);
             nrows++;
         }
     }
-    
-    for (l1 = 0; l1 < arpData->seqWidgetCount(); l1++) {
-        ccList = arpData->seqWidget(l1)->ccList;
-        
-        for (l2 = 0; l2 < arpData->seqWidget(l1)->ccList.count(); l2++) {
-        
+
+    for (l1 = 0; l1 < engine->seqWidgetCount(); l1++) {
+        ccList = engine->seqWidget(l1)->ccList;
+
+        for (l2 = 0; l2 < engine->seqWidget(l1)->ccList.count(); l2++) {
+
             midiCCTable->setVerticalHeaderItem(nrows,
-                    new QTableWidgetItem(arpData->seqWidget(l1)->name));
-                    
-            fillControlRow(nrows, ccList.at(l2), arpData->seqWidget(l1)->ID);
+                    new QTableWidgetItem(engine->seqWidget(l1)->name));
+
+            fillControlRow(nrows, ccList.at(l2), engine->seqWidget(l1)->ID);
             nrows++;
         }
     }
@@ -112,23 +116,23 @@ void MidiCCTable::getCurrentControls()
     midiCCTable->setHorizontalHeaderItem(0,
             new QTableWidgetItem(tr("Control")));
     midiCCTable->setColumnWidth(0, 80);
-    
+
     midiCCTable->setHorizontalHeaderItem(1,
             new QTableWidgetItem(tr("CC#")));
     midiCCTable->setColumnWidth(1, 30);
-    
+
     midiCCTable->setHorizontalHeaderItem(2,
             new QTableWidgetItem(tr("Ch")));
     midiCCTable->setColumnWidth(2, 30);
-    
+
     midiCCTable->setHorizontalHeaderItem(3,
             new QTableWidgetItem(tr("min")));
     midiCCTable->setColumnWidth(3, 30);
-    
+
     midiCCTable->setHorizontalHeaderItem(4,
             new QTableWidgetItem(tr("max")));
     midiCCTable->setColumnWidth(4, 30);
-    
+
     midiCCTable->setRowCount(nrows);
 }
 
@@ -143,14 +147,14 @@ void MidiCCTable::apply()
     int ccnumber, channel, min, max, ctrlID, moduleID;
     int l1;
     QChar moduleType;
-    
-    for (l1 = 0; l1 < arpData->arpWidgetCount(); l1++) 
-            arpData->arpWidget(l1)->ccList.clear();
-    for (l1 = 0; l1 < arpData->lfoWidgetCount(); l1++) 
-            arpData->lfoWidget(l1)->ccList.clear();
-    for (l1 = 0; l1 < arpData->seqWidgetCount(); l1++) 
-            arpData->seqWidget(l1)->ccList.clear();
-            
+
+    for (l1 = 0; l1 < engine->arpWidgetCount(); l1++)
+            engine->arpWidget(l1)->ccList.clear();
+    for (l1 = 0; l1 < engine->lfoWidgetCount(); l1++)
+            engine->lfoWidget(l1)->ccList.clear();
+    for (l1 = 0; l1 < engine->seqWidgetCount(); l1++)
+            engine->seqWidget(l1)->ccList.clear();
+
     for (l1 = 0; l1 < midiCCTable->rowCount(); l1++) {
         ccnumber = midiCCTable->item(l1, 1)->text().toInt();
         channel = midiCCTable->item(l1, 2)->text().toInt() - 1;
@@ -159,18 +163,18 @@ void MidiCCTable::apply()
         ctrlID = midiCCTable->item(l1, 5)->text().toInt();
         moduleID = midiCCTable->item(l1, 6)->text().toInt();
         moduleType = midiCCTable->verticalHeaderItem(l1)->text().at(0);
-        
+
         switch (moduleType.toLatin1()) {
-            case 'A': 
-                    arpData->arpWidget(moduleID)
+            case 'A':
+                    engine->arpWidget(moduleID)
                     ->appendMidiCC(ctrlID, ccnumber, channel, min, max);
             break;
-            case 'L': 
-                    arpData->lfoWidget(moduleID)
+            case 'L':
+                    engine->lfoWidget(moduleID)
                     ->appendMidiCC(ctrlID, ccnumber, channel, min, max);
             break;
-            case 'S': 
-                    arpData->seqWidget(moduleID)
+            case 'S':
+                    engine->seqWidget(moduleID)
                     ->appendMidiCC(ctrlID, ccnumber, channel, min, max);
             break;
         }
@@ -191,10 +195,10 @@ void MidiCCTable::closeEvent(QCloseEvent *e)
 void MidiCCTable::itemChanged(QTableWidgetItem *item)
 {
     int test, row, comp;
-    
+
     row = midiCCTable->currentRow();
     test = item->text().toInt();
-    
+
     switch (midiCCTable->currentColumn()) {
         case 1: // CC Number
                 if (test > 127) item->setText("127");
@@ -214,20 +218,20 @@ void MidiCCTable::itemChanged(QTableWidgetItem *item)
                     if (test > 127) item->setText("127");
                     if (test < comp) item->setText(QString::number(comp));
         break;
-        default: 
+        default:
         break;
     }
 }
 void MidiCCTable::fillControlRow(int row, MidiCC midiCC, int moduleID)
 {
     QTableWidgetItem *nameItem = new QTableWidgetItem(midiCC.name);
-    nameItem->setFlags(Qt::ItemFlags(Qt::ItemIsSelectable 
+    nameItem->setFlags(Qt::ItemFlags(Qt::ItemIsSelectable
                                     |Qt::ItemIsEnabled));
     nameItem->setBackground(QBrush(QColor(200,200,200)));
     midiCCTable->setItem(row, 0, nameItem);
-    midiCCTable->setItem(row, 1, 
+    midiCCTable->setItem(row, 1,
             new QTableWidgetItem(QString::number(midiCC.ccnumber)));
-    midiCCTable->setItem(row, 2, 
+    midiCCTable->setItem(row, 2,
             new QTableWidgetItem(QString::number(midiCC.channel + 1)));
     midiCCTable->setItem(row, 3,
             new QTableWidgetItem(QString::number(midiCC.min)));
@@ -237,13 +241,13 @@ void MidiCCTable::fillControlRow(int row, MidiCC midiCC, int moduleID)
             new QTableWidgetItem(QString::number(midiCC.ID)));
     midiCCTable->setItem(row, 6,
             new QTableWidgetItem(QString::number(moduleID)));
-    
+
     midiCCTable->setRowHeight(row, 20);
 }
 
 void MidiCCTable::revert()
 {
-    midiCCTable->setRowCount(arpData->moduleWindowCount()*10);
+    midiCCTable->setRowCount(engine->moduleWindowCount()*10);
     getCurrentControls();
 }
 
diff --git a/src/midicctable.h b/src/midicctable.h
index 7f8c263..4164cfc 100644
--- a/src/midicctable.h
+++ b/src/midicctable.h
@@ -1,22 +1,26 @@
-/*
- *      midicctable.h
- *      
+/*!
+ * @file midicctable.cpp
+ * @brief Header for the MidiCCTable QDialog class
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
 #ifndef MIDICCTABLE_H
@@ -25,15 +29,23 @@
 #include <QDialog>
 #include <QTableWidget>
 #include <QPushButton>
-#include "arpdata.h"
+#include "engine.h"
 
+/*! @brief QDialog class for managing MIDI controller mappings
+ *
+ * MidiCCTable uses a QTableWidget to display and modify the content of
+ * all MIDI controllers attributed to QMidiArp's UI elements. It is
+ * instantiated on program start by MainWindow and shown when the MainWindow
+ * menu entry is selected. Calling the menu entry will also cause retrieval
+ * and display of all modules' CCLists.
+ */
 class MidiCCTable : public QDialog
 {
     Q_OBJECT
 
  public:
 
-    MidiCCTable(ArpData *p_arpData, QWidget *parent);
+    MidiCCTable(Engine *p_engine, QWidget *parent);
     ~MidiCCTable();
 
  private:
@@ -41,8 +53,8 @@ class MidiCCTable : public QDialog
     QPushButton *removeButton, *revertButton;
     void getCurrentControls();
     void fillControlRow(int nrows, MidiCC midiCC, int moduleID);
-    ArpData *arpData;
-    
+    Engine *engine;
+
  public slots:
     void accept();
     void reject();
diff --git a/src/midievent.h b/src/midievent.h
new file mode 100644
index 0000000..cf951f6
--- /dev/null
+++ b/src/midievent.h
@@ -0,0 +1,84 @@
+/*!
+ * @file midievent.h
+ * @brief Defines the MidiEvent structure and the midi_event_type enum
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
+
+#ifndef MIDIEVENT_H
+#define MIDIEVENT_H
+
+/*! @brief Structure holding elements of a MIDI event
+ */
+struct MidiEvent {
+        int type;
+        int channel;
+        int data;
+        int value;
+    };
+
+#endif
+
+/*! @brief Sequencer event type enum in analogy to the ALSA snd_seq_event_types */
+enum midi_event_type {
+    EV_SYSTEM = 0,
+    EV_RESULT,
+
+    EV_NOTE = 5,
+    EV_NOTEON,
+    EV_NOTEOFF,
+    EV_KEYPRESS,
+
+    EV_CONTROLLER = 10,
+    EV_PGMCHANGE,
+    EV_CHANPRESS,
+    EV_PITCHBEND,
+    EV_CONTROL14,
+    EV_NONREGPARAM,
+    EV_REGPARAM,
+
+    EV_SONGPOS = 20,
+    EV_SONGSEL,
+    EV_QFRAME,
+    EV_TIMESIGN,
+    EV_KEYSIGN,
+
+    EV_START = 30,
+    EV_CONTINUE,
+    EV_STOP,
+    EV_SETPOS_TICK,
+    EV_SETPOS_TIME,
+    EV_TEMPO,
+    EV_CLOCK,
+    EV_TICK,
+    EV_QUEUE_SKEW,
+    EV_SYNC_POS,
+
+    EV_TUNE_REQUEST = 40,
+    EV_RESET,
+    EV_SENSING,
+
+    EV_ECHO = 50,
+    EV_SYSEX = 130,
+    EV_BOUNCE,
+
+    EV_NONE = 255
+};
diff --git a/src/midilfo.cpp b/src/midilfo.cpp
index 8329795..526249d 100644
--- a/src/midilfo.cpp
+++ b/src/midilfo.cpp
@@ -1,24 +1,27 @@
-/*
- *      midilfo.cpp
- *      
+/*!
+ * @file midilfo.cpp
+ * @brief Implements the MidiLfo MIDI worker class for the LFO Module.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
-
 #include <cmath>
 #include "midilfo.h"
 
@@ -31,26 +34,32 @@ MidiLfo::MidiLfo()
     freq = 4;
     size = 1;
     res = 16;
+    old_res = 0;
     ccnumber = 74;
     portOut = 0;
     channelOut = 0;
+    chIn = 0;
+    ccnumberIn = 74;
     waveFormIndex = 0;
     isMuted = false;
+    recordMode = false;
+    isRecording = false;
+    recValue = 0;
     int l1 = 0;
     int lt = 0;
-    int step = TICKS_PER_QUARTER / res;
-    LfoSample lfoSample;
-    lfoSample.value = 63;
+    int step = TPQN / res;
+    Sample sample;
+    sample.value = 63;
     customWave.clear();
     cwmin = 0;
     for (l1 = 0; l1 < size * res; l1++) {
-            lfoSample.tick = lt;
-            lfoSample.muted = false;
-            customWave.append(lfoSample);
+            sample.tick = lt;
+            sample.muted = false;
+            customWave.append(sample);
             lt+=step;
     }
     muteMask.fill(false, size * res);
-    lfoData.clear();
+    data.clear();
     lastMouseLoc = 0;
     lastMouseY = 0;
     frameptr = 0;
@@ -64,74 +73,101 @@ void MidiLfo::setMuted(bool on)
     isMuted = on;
 }
 
-void MidiLfo::getNextFrame(QVector<LfoSample> *p_lfoData)
+void MidiLfo::getNextFrame(QVector<Sample> *p_data)
 {
-    //this function is called by seqdriver and returns a frame of
-    //maximum LFO_FRAMESIZE points
-    
-    QVector<LfoSample> lfoFrame;
-    LfoSample lfoSample;
-    int step = TICKS_PER_QUARTER / res;
+    //this function is called by seqdriver and returns one sample
+    //if res <= LFO_FRAMELIMIT. If res > LFO_FRAMELIMIT, a frame is output
+    //The FRAMELIMIT avoids excessive cursor updating
+
+    QVector<Sample> frame;
+    Sample sample;
+    int step = TPQN / res;
     int npoints = size * res;
     int lt, l1;
-    
-    lfoFrame.clear();
-    lt = 0;
+    int framelimit;
+    int framesize;
+    int index;
+
+    frame.clear();
+
+    if (isRecording) framelimit = 32; else framelimit = LFO_FRAMELIMIT;
+    framesize = res / framelimit;
     l1 = 0;
-    
-    while ((l1 < LFO_FRAMESIZE) && (l1 < npoints)) {
-        lfoSample = lfoData.at((l1 + frameptr) % npoints);
-        lfoSample.tick = lt;
-        lfoFrame.append(lfoSample);
+    lt = 0;
+
+    do {
+        index = (l1 + frameptr) % npoints;
+        sample = data.at(index);
+        if (isRecording) {
+            if (!framesize) {
+                sample.value = recValue;
+            }
+            else {
+            /** We do linear interpolation of points within frames if
+             * framesize is > 0 to get a smooth recording at high resolutions
+             * interpolation is linear between lastSampleValue and current recValue
+             * */
+                sample.value = lastSampleValue
+                            + (double)(recValue - lastSampleValue) / res * framelimit
+                            * ((double)l1 + .5);
+            }
+            customWave.replace(index, sample);
+        }
+        sample.tick = lt;
+        frame.append(sample);
         lt+=step;
         l1++;
-    }
-    lfoSample.value = -1;
-    lfoSample.tick = lt;
-    lfoFrame.append(lfoSample);    
-   
+    } while ((l1 < framesize) & (l1 < npoints));
+
+    lastSampleValue = recValue;
+
+    sample.value = -1;
+    sample.tick = lt;
+    frame.append(sample);
+    emit nextStep(frameptr);
+
     frameptr += l1;
     frameptr %= npoints;
-    
-    *p_lfoData = lfoFrame;
+
+    *p_data = frame;
 }
 
-void MidiLfo::getData(QVector<LfoSample> *p_lfoData)
-{ 
+void MidiLfo::getData(QVector<Sample> *p_data)
+{
     //this function returns the full LFO wave
-    
-    LfoSample lfoSample;
+
+    Sample sample;
     int l1 = 0;
     int lt = 0;
-    int step = TICKS_PER_QUARTER / res;
+    int step = TPQN / res;
     int val = 0;
     int tempval;
     bool cl = false;
     int npoints = size * res;
     //res: number of events per beat
     //size: size of waveform in beats
-    
-    lfoData.clear();
-    
+
+    data.clear();
+
     switch(waveFormIndex) {
         case 0: //sine
             for (l1 = 0; l1 < npoints; l1++) {
-                lfoSample.value = clip((-cos((double)(l1 * 6.28 / 
+                sample.value = clip((-cos((double)(l1 * 6.28 /
                 res * freq / 4)) + 1) * amp / 2 + offs, 0, 127, &cl);
-                lfoSample.tick = lt;
-                lfoSample.muted = muteMask.at(l1);
-                lfoData.append(lfoSample);
+                sample.tick = lt;
+                sample.muted = muteMask.at(l1);
+                data.append(sample);
                 lt += step;
             }
         break;
         case 1: //sawtooth up
             val = 0;
             for (l1 = 0; l1 < npoints; l1++) {
-                lfoSample.value = clip(val * amp / res / 4 
+                sample.value = clip(val * amp / res / 4
                 + offs, 0, 127, &cl);
-                lfoSample.tick = lt;
-                lfoSample.muted = muteMask.at(l1);
-                lfoData.append(lfoSample);
+                sample.tick = lt;
+                sample.muted = muteMask.at(l1);
+                data.append(sample);
                 lt += step;
                 val += freq;
                 val %= res * 4;
@@ -142,11 +178,11 @@ void MidiLfo::getData(QVector<LfoSample> *p_lfoData)
             for (l1 = 0; l1 < npoints; l1++) {
                 tempval = val - res * 2;
                 if (tempval < 0 ) tempval = -tempval;
-                lfoSample.value = clip((res * 2 - tempval) * amp 
+                sample.value = clip((res * 2 - tempval) * amp
                         / res / 2 + offs, 0, 127, &cl);
-                lfoSample.tick = lt;
-                lfoSample.muted = muteMask.at(l1);
-                lfoData.append(lfoSample);
+                sample.tick = lt;
+                sample.muted = muteMask.at(l1);
+                data.append(sample);
                 lt += step;
                 val += freq;
                 val %= res * 4;
@@ -155,11 +191,11 @@ void MidiLfo::getData(QVector<LfoSample> *p_lfoData)
         case 3: //sawtooth down
             val = 0;
             for (l1 = 0; l1 < npoints; l1++) {
-                lfoSample.value = clip((res * 4 - val) 
+                sample.value = clip((res * 4 - val)
                         * amp / res / 4 + offs, 0, 127, &cl);
-                lfoSample.tick = lt;
-                lfoSample.muted = muteMask.at(l1);
-                lfoData.append(lfoSample);
+                sample.tick = lt;
+                sample.muted = muteMask.at(l1);
+                data.append(sample);
                 lt+=step;
                 val += freq;
                 val %= res * 4;
@@ -167,25 +203,25 @@ void MidiLfo::getData(QVector<LfoSample> *p_lfoData)
         break;
         case 4: //square
             for (l1 = 0; l1 < npoints; l1++) {
-                lfoSample.value = clip(amp * ((l1 * freq / 2 
+                sample.value = clip(amp * ((l1 * freq / 2
                         / res) % 2 == 0) + offs, 0, 127, &cl);
-                lfoSample.tick = lt;
-                lfoSample.muted = muteMask.at(l1);
-                lfoData.append(lfoSample);
+                sample.tick = lt;
+                sample.muted = muteMask.at(l1);
+                data.append(sample);
                 lt+=step;
             }
         break;
         case 5: //custom
             lt = step * customWave.count();
-            lfoData = customWave;
+            data = customWave;
         break;
         default:
         break;
     }
-    lfoSample.value = -1;
-    lfoSample.tick = lt;
-    lfoData.append(lfoSample);    
-    *p_lfoData = lfoData;
+    sample.value = -1;
+    sample.tick = lt;
+    data.append(sample);
+    *p_data = data;
 }
 
 int MidiLfo::clip(int value, int min, int max, bool *outOfRange)
@@ -199,7 +235,7 @@ int MidiLfo::clip(int value, int min, int max, bool *outOfRange)
     } else if (tmp < min) {
         tmp = min;
         *outOfRange = true;
-    }  
+    }
     return(tmp);
 }
 
@@ -220,9 +256,22 @@ void MidiLfo::updateAmplitude(int val)
 
 void MidiLfo::updateOffset(int val)
 {
+    if (waveFormIndex == 5) updateCustomWaveOffset(val);
     offs = val;
 }
 
+void MidiLfo::updateResolution(int val)
+{
+    res = val;
+    resizeAll();
+}
+
+void MidiLfo::updateSize(int val)
+{
+    size = val;
+    resizeAll();
+}
+
 void MidiLfo::updateQueueTempo(int val)
 {
     queueTempo = (double)val;
@@ -230,29 +279,32 @@ void MidiLfo::updateQueueTempo(int val)
 
 void MidiLfo::setCustomWavePoint(double mouseX, double mouseY, bool newpt)
 {
-    LfoSample lfoSample;
+    Sample sample;
     int loc = mouseX * (res * size);
     int Y = mouseY * 128;
-    
+
     if (newpt) {
+    // the mouse was just clicked so we can directly set the point
         lastMouseLoc = loc;
         lastMouseY = Y;
     }
-    
+
     if (loc == lastMouseLoc) lastMouseY = Y;
-    
+
     do {
+    //if the mouse was moved, we interpolate potentially missing points after
+    //the last mouse position
         if (loc > lastMouseLoc) {
             lastMouseY += (double)(lastMouseY - Y) / (lastMouseLoc - loc) + .5;
             lastMouseLoc++;
-        } 
+        }
         if (loc < lastMouseLoc) {
             lastMouseY -= (double)(lastMouseY - Y) / (lastMouseLoc - loc) - .5;
             lastMouseLoc--;
         }
-        lfoSample = customWave.at(lastMouseLoc);
-        lfoSample.value = lastMouseY;
-        customWave.replace(lastMouseLoc, lfoSample);
+        sample = customWave.at(lastMouseLoc);
+        sample.value = lastMouseY;
+        customWave.replace(lastMouseLoc, sample);
     } while (lastMouseLoc != loc);
 }
 
@@ -261,17 +313,18 @@ void MidiLfo::resizeAll()
     int lt = 0;
     int l1 = 0;
     int os;
-    int step = TICKS_PER_QUARTER / res;
-    LfoSample lfoSample;
-    
+    int step = TPQN / res;
+    Sample sample;
+
+    frameptr%=(res * size);
     os = customWave.count();
     customWave.resize(size * res);
     muteMask.resize(size * res);
     for (l1 = 0; l1 < customWave.count(); l1++) {
-        lfoSample = customWave.at(l1 % os);
-        lfoSample.tick = lt;
-        lfoSample.muted = muteMask.at(l1);
-        customWave.replace(l1, lfoSample);
+        sample = customWave.at(l1 % os);
+        sample.tick = lt;
+        sample.muted = muteMask.at(l1);
+        customWave.replace(l1, sample);
         lt+=step;
     }
 }
@@ -279,47 +332,47 @@ void MidiLfo::resizeAll()
 void MidiLfo::copyToCustom()
 {
     int m;
-    
-    m = lfoData.count();
-    lfoData.remove(m - 1);
-    customWave = lfoData;
+
+    m = data.count();
+    data.remove(m - 1);
+    customWave = data;
 }
 
 void MidiLfo::updateCustomWaveOffset(int cwoffs)
 {
-    LfoSample lfoSample;
+    Sample sample;
     const int count = customWave.count();
     int l1 = 0;
     bool cl = false;
-    
+
     while ((!cl) && (l1 < count)) {
-        lfoSample.value = clip(customWave.at(l1).value + cwoffs - cwmin,
+        sample.value = clip(customWave.at(l1).value + cwoffs - cwmin,
                             0, 127, &cl);
         l1++;
         }
-        
+
     if (cl) return;
-    
+
     for (l1 = 0; l1 < count; l1++) {
-        lfoSample = customWave.at(l1);
-        lfoSample.value += cwoffs - cwmin;
-        customWave.replace(l1, lfoSample);
+        sample = customWave.at(l1);
+        sample.value += cwoffs - cwmin;
+        customWave.replace(l1, sample);
     }
     cwmin = cwoffs;
 }
 
 bool MidiLfo::toggleMutePoint(double mouseX)
 {
-    LfoSample lfoSample;
+    Sample sample;
     bool m;
     int loc = mouseX * (res * size);
-    
+
     m = muteMask.at(loc);
     muteMask.replace(loc, !m);
     if (waveFormIndex == 5) {
-        lfoSample = customWave.at(loc);
-        lfoSample.muted = !m;
-        customWave.replace(loc, lfoSample);
+        sample = customWave.at(loc);
+        sample.muted = !m;
+        customWave.replace(loc, sample);
     }
     lastMouseLoc = loc;
     return(!m);
@@ -327,17 +380,17 @@ bool MidiLfo::toggleMutePoint(double mouseX)
 
 void MidiLfo::setMutePoint(double mouseX, bool on)
 {
-    LfoSample lfoSample;
+    Sample sample;
     int loc = mouseX * (res * size);
-    
+
     do {
         if (waveFormIndex == 5) {
-            lfoSample = customWave.at(lastMouseLoc);
-            lfoSample.muted = on;
-            customWave.replace(lastMouseLoc, lfoSample);
+            sample = customWave.at(lastMouseLoc);
+            sample.muted = on;
+            customWave.replace(lastMouseLoc, sample);
         }
         muteMask.replace(lastMouseLoc, on);
-        if (loc > lastMouseLoc) lastMouseLoc++; 
+        if (loc > lastMouseLoc) lastMouseLoc++;
         if (loc < lastMouseLoc) lastMouseLoc--;
     } while (lastMouseLoc != loc);
 }
@@ -346,3 +399,17 @@ void MidiLfo::resetFramePtr()
 {
     frameptr = 0;
 }
+
+void MidiLfo::record(int value)
+{
+    recValue = value;
+    isRecording = true;
+}
+
+bool MidiLfo::wantEvent(MidiEvent inEv)
+{
+    if (!recordMode) return(false);
+    if (inEv.channel != chIn) return(false);
+    if (inEv.data != ccnumberIn) return(false);
+    return(true);
+}
diff --git a/src/midilfo.h b/src/midilfo.h
index 635a61e..6c95eb2 100644
--- a/src/midilfo.h
+++ b/src/midilfo.h
@@ -1,22 +1,26 @@
-/*
- *      midilfo.h
- *      
+/*!
+ * @file midilfo.h
+ * @brief Member definitions for the MidiLfo MIDI worker class.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
 #ifndef MIDILFO_H
@@ -25,56 +29,207 @@
 #include <QObject>
 #include <QString>
 #include <QVector>
-
+#include <alsa/asoundlib.h>
 #include <main.h>
 
-    struct LfoSample {
+#ifndef SAMPLE_H
+#define SAMPLE_H
+
+/*! @brief Structure holding elements of a MIDI note or controller representing
+ * one point of a waveform
+ */
+    struct Sample {
         int value;
         int tick;
         bool muted;
-    };    
+    };
+#endif
 
+/*! @brief MIDI worker class for the LFO Module. Implements a sequencer
+ * for controller data as a QObject.
+ *
+ * The parameters of MidiLfo are controlled by the LfoWidget class.
+ * A pointer to MidiLfo is passed to the SeqDriver thread, which calls
+ * the MidiLfo::getNextFrame member as a function of the position of
+ * the ALSA queue. MidiLfo will return an array of controller values
+ * representing a frame of its internal MidiLfo::data buffer. This frame
+ * has size 1 except for resolution higher than 16th notes.
+ * The MidiLfo::data buffer is populated by the MidiLfo::getData function
+ * at each modification done via the LfoWidget. It can consist of
+ * a classic waveform calculation or a hand-drawn waveform. In all cases
+ * the waveform has resolution, offset and size attributes and single
+ * points can be tagged as muted, which will avoid data output at the
+ * corresponding position.
+ */
 class MidiLfo : public QObject  {
-    
+
   Q_OBJECT
 
   private:
-    double queueTempo;
-    int lastMouseLoc, lastMouseY;
-    int frameptr;
+    double queueTempo;  /*!< current tempo of the ALSA queue, not in use here */
+    int lastMouseLoc;   /*!< The X location of the last modification of the wave, used for interpolation*/
+    int lastMouseY;     /*!< The Y location at the last modification of the wave, used for interpolation*/
+    int frameptr;       /*!< position of the currently output frame in the MidiArp::data waveform */
+    int recValue;
+    int lastSampleValue;
+/**
+ * @brief This function allows forcing an integer value within the
+ * specified range (clip).
+ *
+ * @param value The value to be checked
+ * @param min The minimum allowed return value
+ * @param max The maximum allowed return value
+ * @param outOfRange Is set to True if value was outside min|max range
+ * @return The value clipped within the range
+ */
     int clip(int value, int min, int max, bool *outOfRange);
-    QVector<LfoSample> lfoData;
-     
+    QVector<Sample> data;
+/*! @brief This function recalculates the MidiLfo::customWave as a function
+ * of a new offset value.
+ *
+ * It is called by MidiLfo::updateOffset() in case a custom wave is active.
+ * @param cwoffs New offset value
+ */
+    void updateCustomWaveOffset(int cwoffs);
+
   public:
-    int portOut;    // Output port (ALSA Sequencer)
-    int channelOut;
-    bool hold, isMuted;
-    int freq, amp, offs, ccnumber;
-    int size, res, waveFormIndex;
-    int cwmin;
-    QVector<LfoSample> customWave;
-    QVector<bool> muteMask;
-           
+    int portOut;    /*!< ALSA output port number */
+    int channelOut; /*!< ALSA output channel */
+    bool recordMode, isRecording;
+    int old_res;
+    int ccnumber;   /*!< MIDI Controller CC number to output */
+    bool isMuted;   /*!< Global mute state */
+    int freq, amp, offs, ccnumberIn, chIn;
+    int size;       /*!< Size of the waveform in quarter notes */
+    int res;        /*!< Resolution of the waveform in ticks per quarter note */
+    int waveFormIndex;          /*!< Index of the waveform to produce
+                                    @par 0: Sine
+                                    @par 1: Sawtooth Up
+                                    @par 2: Triangle
+                                    @par 3: Sawtooth Down
+                                    @par 4: Square
+                                    @par 5: Use Custom Wave */
+    int cwmin;                  /*!< The minimum of MidiLfo::customWave */
+    QVector<Sample> customWave; /*!< Vector of Sample points holding the custom drawn wave */
+    QVector<bool> muteMask;     /*!< Vector of booleans with mute state information for each wave point */
+
   public:
     MidiLfo();
     ~MidiLfo();
-    void getData(QVector<LfoSample> *lfoData);  
-    void getNextFrame(QVector<LfoSample> *p_lfoData);
-    bool toggleMutePoint(double);
-    
-  public slots:  
+    void updateWaveForm(int val);
     void updateFrequency(int);
     void updateAmplitude(int);
     void updateOffset(int);
-    void updateCustomWaveOffset(int);
+    void updateResolution(int);
+    void updateSize(int);
     void updateQueueTempo(int);
-    void setMuted(bool); //set mute
-    void updateWaveForm(int val);
-    void setCustomWavePoint(double, double, bool);
-    void setMutePoint(double, bool);
+    void record(int value);
+/*! @brief This function sets MidiLfo::isMuted, which is checked by
+ * SeqDriver and which suppresses data output globally if set to True.
+ *
+ * @param on Set to True to suppress data output to ALSA
+ */
+    void setMuted(bool on);
+/*! @brief This function sets the (controller) value of one point of the
+ * MidiLfo::customWave array. It is used for handling drawing functionality.
+ *
+ * The member is called by LfoWidget::mouseMoved or LfoWidget::mousePressed.
+ * The normalized mouse coordinates are scaled to the waveform size and
+ * resolution and to the controller range (0 ... 127). The function
+ * interpolates potentially missing waveform points between two events
+ * if the mouse buttons were not released.
+ *
+ * @param mouseX Normalized horizontal location of the mouse on the
+ * LfoScreen (0.0 ... 1.0)
+ * @param mouseY Normalized verical location of the mouse on the
+ * LfoScreen (0.0 ... 1.0)
+ * @param newpt Set to true if the mouse button was newly clicked before
+ * the move
+ *
+ * @see MidiLfo::toggleMutePoint(), MidiLfo::setMutePoint()
+ */
+    void setCustomWavePoint(double mouseX, double mouseY, bool newpt);
+/*! @brief This function sets the mute state of one point of the
+ * MidiLfo::muteMask array to the given state.
+ *
+ * The member is called when the right mouse button is clicked on the
+ * LfoScreen.
+ * If calculated waveforms are active, only the MidiLfo::muteMask is
+ * changed. If a custom waveform is active, the Sample.mute status
+ * at the given position is changed as well.
+ *
+ * @param mouseX Normalized Horizontal location of the mouse on the
+ * LfoScreen (0.0 ... 1.0)
+ * @param muted mute state to set for the given position
+ *
+ * @see MidiLfo::toggleMutePoint()
+ */
+    void setMutePoint(double mouseX, bool muted);
+/*! @brief This function recalculates the MidiLfo::customWave as a
+ * function of the current MidiLfo::res and MidiLfo::size values.
+ *
+ * It is called upon every change of MidiLfo::size and MidiLfo::res. It
+ * repeats the current MidiLfo::customWave periodically if the new values
+ * lead to a bigger size data array.
+ */
     void resizeAll();
+/*! @brief This function copies the current MidiLfo::data array into
+ * MidiLfo::customWave.
+ *
+ * It is called when a waveform modification by the user is attempted
+ * while in calculated waveform mode. (MidiLfo::waveFormIndex 1 ... 4).
+ */
     void copyToCustom();
+/*! @brief This function resets the MidiLfo::frameptr to zero.
+ *
+ * It is called when the ALSA queue starts.
+ */
     void resetFramePtr();
+/**
+ * @brief This function checks whether an ALSA event is eligible for this
+ * module.
+ *
+ * Its response depends on the input filter settings, i.e. note,
+ * velocity and channel.
+ *
+ * @param inEv MidiEvent to check
+ * @return True if inEv is in the input range of the module
+ */
+    bool wantEvent(MidiEvent inEv);
+/*! @brief This function is the main calculator for the data contained
+ * in a waveform.
+ *
+ * It is called upon every change of parameters in LfoWidget or upon
+ * input by mouse clicks on the LfoScreen. It fills the
+ * MidiLfo::data buffer with Sample points, which it either calculates
+ * or which it copies from the MidiLfo::customWave data.
+ *
+ * @param *data reference to an array the waveform is copied to
+ */
+    void getData(QVector<Sample> *data);
+/*! @brief This function transfers a frame of Sample data points taken from
+ * the currently active waveform MidiLfo::data.
+ *
+ * @param *p_data reference to an array the frame is copied to
+ */
+    void getNextFrame(QVector<Sample> *p_data);
+/*! @brief This function toggles the mute state of one point of the
+ * MidiLfo::muteMask array.
+ *
+ * The member is called when the right mouse button is clicked on the
+ * LfoScreen.
+ * If calculated waveforms are active, only the MidiLfo::muteMask is
+ * changed. If a custom waveform is active, the Sample.mute status
+ * at the given position is changed as well.
+ *
+ * @param mouseX Normalized Horizontal location of the mouse on the
+ * LfoScreen (0.0 ... 1.0)
+ * @see MidiLfo::setMutePoint
+ */
+    bool toggleMutePoint(double mouseX);
+
+  signals:
+    void nextStep(int frameptr);
 };
-                              
+
 #endif
diff --git a/src/midiseq.cpp b/src/midiseq.cpp
index f128e93..8c85926 100644
--- a/src/midiseq.cpp
+++ b/src/midiseq.cpp
@@ -1,34 +1,44 @@
-/*
- *      midiseq.cpp
- *      
+/*!
+ * @file midiseq.cpp
+ * @brief Implements the MidiSeq MIDI worker class for the Seq Module.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
-
 #include <cmath>
-#include <alsa/asoundlib.h>
 #include "midiseq.h"
 
 
 MidiSeq::MidiSeq()
 {
     enableNoteIn = true;
+    enableNoteOff = false;
     enableVelIn = true;
+    recordMode = false;
+    trigByKbd = false;
+    restartByKbd = false;
+    enableLoop = false;
     currentRecStep = 0;
+    seqFinished = false;
+    noteCount = 0;
+
     chIn = 0;
     queueTempo = 100.0;
     vel = 0;
@@ -39,17 +49,18 @@ MidiSeq::MidiSeq()
     portOut = 0;
     channelOut = 0;
     waveFormIndex = 0;
+    currentIndex = 0;
     isMuted = false;
     int lt = 0;
     int l1 = 0;
-    int step = TICKS_PER_QUARTER / res;
-    SeqSample seqSample;
-    seqSample.value = 60;
+    int step = TPQN / res;
+    Sample sample;
+    sample.value = 60;
     customWave.clear();
     for (l1 = 0; l1 < size * res; l1++) {
-            seqSample.tick = lt;
-            seqSample.muted = false;
-            customWave.append(seqSample);
+            sample.tick = lt;
+            sample.muted = false;
+            customWave.append(sample);
             lt+=step;
     }
     muteMask.fill(false, size * res);
@@ -63,54 +74,83 @@ void MidiSeq::setMuted(bool on)
     isMuted = on;
 }
 
-bool MidiSeq::isSeq(snd_seq_event_t *evIn) {
+bool MidiSeq::wantEvent(MidiEvent inEv) {
 
-    if ((evIn->type != SND_SEQ_EVENT_NOTEON)
-            && (evIn->type != SND_SEQ_EVENT_NOTEOFF)
-            && (evIn->type != SND_SEQ_EVENT_CONTROLLER)) 
-    {
-        return(false);
-    }
-    if ((evIn->data.control.channel < chIn)
-            || (evIn->data.control.channel > chIn)) 
-    {
-        return(false);
+    if ((inEv.type != EV_NOTEON) && (inEv.type != EV_CONTROLLER)) return(false);
+
+    if (inEv.channel != chIn) return(false);
+
+    if ((inEv.type == EV_NOTEON)) {
+        if ((inEv.data < 36) || (inEv.data >= 84)) return(false);
     }
-    if ((evIn->type == SND_SEQ_EVENT_NOTEON) 
-            || (evIn->type == SND_SEQ_EVENT_NOTEOFF)) {
-        if (!(enableNoteIn)) {
-            return(false);
+    return(true);
+}
+
+void MidiSeq::handleNote(int note, int velocity, int tick)
+{
+    if (recordMode) recordNote(note);
+
+    else {
+        if (velocity) {
+            /**This is a NOTE ON event*/
+            if (enableNoteIn) updateTranspose(note - 60);
+            if (restartByKbd && !noteCount) currentIndex = 0;
+            if (enableVelIn) updateVelocity(velocity);
+            seqFinished = false;
+            noteCount++;
         }
-        if ((evIn->data.note.note < 36) || (evIn->data.note.note >= 84)) {
-            return(false);
+        else {
+            /**This is a NOTE OFF event*/
+            if (enableNoteOff && (noteCount == 1)) seqFinished = true;
+            if (noteCount) noteCount--;
         }
     }
-    return(true);
+
+    if (velocity) emit noteEvent(note, velocity);
+}
+
+bool MidiSeq::wantTrigByKbd()
+{
+    bool on = (trigByKbd && (noteCount == 1));
+    return(on);
+}
+
+
+void MidiSeq::getNextNote(Sample *p_sample)
+{
+    Sample sample;
+    sample = customWave.at(currentIndex);
+    emit nextStep(currentIndex);
+    currentIndex++;
+    currentIndex %= (size * res);
+    if (!enableLoop && !currentIndex) seqFinished = true;
+    if (seqFinished) sample.muted = true;
+    *p_sample = sample;
 }
 
-void MidiSeq::getData(QVector<SeqSample> *p_seqData)
-{ 
-    SeqSample seqSample;
+void MidiSeq::getData(QVector<Sample> *p_data)
+{
+    Sample sample;
     int lt = 0;
-    int step = TICKS_PER_QUARTER / res;
+    int step = TPQN / res;
 
     //res: number of events per beat
     //size: size of waveform in beats
-    QVector<SeqSample> seqData;
-    seqData.clear();
-    
+    QVector<Sample> data;
+    data.clear();
+
     switch(waveFormIndex) {
         case 0: //custom
             lt = step * customWave.count();
-            seqData = customWave;
+            data = customWave;
         break;
         default:
         break;
     }
-    seqSample.value = -1;
-    seqSample.tick = lt;
-    seqData.append(seqSample);    
-    *p_seqData = seqData;
+    sample.value = -1;
+    sample.tick = lt;
+    data.append(sample);
+    *p_data = data;
 }
 
 int MidiSeq::clip(int value, int min, int max, bool *outOfRange)
@@ -124,7 +164,7 @@ int MidiSeq::clip(int value, int min, int max, bool *outOfRange)
     } else if (tmp < min) {
         tmp = min;
         *outOfRange = true;
-    }  
+    }
     return(tmp);
 }
 
@@ -161,14 +201,19 @@ void MidiSeq::setCustomWavePoint(double mouseX, double mouseY)
     setRecordedNote(mouseY * 48 + 36);
 }
 
+void MidiSeq::setRecordMode(int on)
+{
+    recordMode = on;
+}
+
 void MidiSeq::setRecordedNote(int note)
 {
-    SeqSample seqSample;
-        
-    seqSample = customWave.at(currentRecStep);
-    seqSample.value = note;
-    seqSample.tick = currentRecStep * TICKS_PER_QUARTER / res;
-    customWave.replace(currentRecStep, seqSample);
+    Sample sample;
+
+    sample = customWave.at(currentRecStep);
+    sample.value = note;
+    sample.tick = currentRecStep * TPQN / res;
+    customWave.replace(currentRecStep, sample);
 }
 
 void MidiSeq::resizeAll()
@@ -176,17 +221,18 @@ void MidiSeq::resizeAll()
     int lt = 0;
     int l1 = 0;
     int os;
-    int step = TICKS_PER_QUARTER / res;
-    SeqSample seqSample;
-    
+    int step = TPQN / res;
+    Sample sample;
+
+    currentIndex%=(res * size);
     os = customWave.count();
     customWave.resize(size * res);
     muteMask.resize(size * res);
     for (l1 = 0; l1 < customWave.count(); l1++) {
-        seqSample = customWave.at(l1 % os);
-        seqSample.tick = lt;
-        seqSample.muted = muteMask.at(l1);
-        customWave.replace(l1, seqSample);
+        sample = customWave.at(l1 % os);
+        sample.tick = lt;
+        sample.muted = muteMask.at(l1);
+        customWave.replace(l1, sample);
         lt+=step;
     }
     currentRecStep %= (res * size);
@@ -194,37 +240,46 @@ void MidiSeq::resizeAll()
 
 void MidiSeq::copyToCustom()
 {
-    QVector<SeqSample> seqData;
+    QVector<Sample> data;
     int m;
-    
-    seqData.clear();
-    getData(&seqData);
-    m = seqData.count();
-    seqData.remove(m - 1);
-    customWave = seqData;
+
+    data.clear();
+    getData(&data);
+    m = data.count();
+    data.remove(m - 1);
+    customWave = data;
 }
 
 bool MidiSeq::toggleMutePoint(double mouseX)
 {
-    SeqSample seqSample;
+    Sample sample;
     bool m;
     int loc = mouseX * (res * size);
-    
+
     m = muteMask.at(loc);
     muteMask.replace(loc, !m);
-    seqSample = customWave.at(loc);
-    seqSample.muted = !m;
-    customWave.replace(loc, seqSample);
+    sample = customWave.at(loc);
+    sample.muted = !m;
+    customWave.replace(loc, sample);
     return(!m);
 }
 
 void MidiSeq::setMutePoint(double mouseX, bool on)
 {
-    SeqSample seqSample;
+    Sample sample;
     int loc = mouseX * (res * size);
-    
-    seqSample = customWave.at(loc);
-    seqSample.muted = on;
-    customWave.replace(loc, seqSample);
+
+    sample = customWave.at(loc);
+    sample.muted = on;
+    customWave.replace(loc, sample);
     muteMask.replace(loc, on);
 }
+
+void MidiSeq::setCurrentIndex(int ix)
+{
+    currentIndex=ix;
+    if (!ix) {
+        seqFinished = false;
+        noteCount = 0;
+    }
+}
diff --git a/src/midiseq.h b/src/midiseq.h
index 1dd198f..26b7650 100644
--- a/src/midiseq.h
+++ b/src/midiseq.h
@@ -1,22 +1,26 @@
-/*
- *      midiseq.h
- *      
+/*!
+ * @file midiseq.h
+ * @brief Member definitions for the MidiSeq MIDI worker class.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
 #ifndef MIDISEQ_H
@@ -25,56 +29,203 @@
 #include <QObject>
 #include <QString>
 #include <QVector>
-#include <alsa/asoundlib.h>
 #include <main.h>
 
-    struct SeqSample {
+#ifndef SAMPLE_H
+#define SAMPLE_H
+
+/*! @brief Structure holding elements of a MIDI note or controller representing
+ * one point of a waveform
+ */
+    struct Sample {
         int value;
         int tick;
         bool muted;
-    };    
+    };
+#endif
 
+/*! @brief MIDI worker class for the Seq Module. Implements a monophonic
+ * step sequencer as a QObject.
+ *
+ * The parameters of MidiSeq are controlled by the SeqWidget class.
+ * A pointer to MidiSeq is passed to the SeqDriver thread, which calls
+ * the MidiSeq::getNextNote member as a function of the position of
+ * the ALSA queue. MidiSeq will return a note from its internal
+ * MidiSeq::data buffer. The MidiSeq::data buffer is populated by the
+ * MidiSeq::getData function at each modification done via
+ * the SeqWidget. It is modified by drawing a sequence of notes on the
+ * SeqWidget display or by recording incoming notes step by step. In all
+ * cases the sequence has resolution, velocity, note length and
+ * size attributes and single points can be tagged as muted, which will
+ * avoid data output at the corresponding position.
+ */
 class MidiSeq : public QObject  {
-    
+
   Q_OBJECT
 
   private:
     double queueTempo;
     int lastMouseLoc;
+    int currentIndex;
+    int noteCount;
+    bool recordMode;
+    bool seqFinished;
+/**
+ * @brief This function allows forcing an integer value within the
+ * specified range (clip).
+ *
+ * @param value The value to be checked
+ * @param min The minimum allowed return value
+ * @param max The maximum allowed return value
+ * @param outOfRange Is set to True if value was outside min|max range
+ * @return The value clipped within the range
+ */
     int clip(int value, int min, int max, bool *outOfRange);
-     
+
   public:
-    int chIn;       // Channel of input events
-    bool enableNoteIn; // Index input/output (for Controller events)
-    bool enableVelIn; // Parameter that is mapped, [0] low, [1] high boundary
-    int portOut;    // Output port (ALSA Sequencer)
+    int chIn;           /**< Channel of input events */
+    bool enableNoteIn;
+    bool enableNoteOff;
+    bool enableVelIn;
+    bool restartByKbd;
+    bool trigByKbd;
+    bool enableLoop;
+    int portOut;        /**< Output port index */
     int channelOut;
-    bool hold, isMuted;
+    bool isMuted;
     int vel, transp, notelength;
     int size, res, waveFormIndex;
     int currentRecStep;
-    QVector<SeqSample> customWave;
+    QVector<Sample> customWave;
     QVector<bool> muteMask;
-               
+
   public:
     MidiSeq();
     ~MidiSeq();
-    bool isSeq(snd_seq_event_t *evIn);
-    void getData(QVector<SeqSample> *seqData);  
-    bool toggleMutePoint(double);
-    
-  public slots:  
+    void updateWaveForm(int val);
     void updateVelocity(int);
     void updateTranspose(int);
     void updateQueueTempo(int);
+
     void recordNote(int note);
-    void setMuted(bool); //set mute
-    void updateWaveForm(int val);
-    void setCustomWavePoint(double, double);
-    void setMutePoint(double, bool);
+/*! @brief This function sets MidiLfo::isMuted, which is checked by
+ * SeqDriver and which suppresses data output globally if set to True.
+ *
+ * @param on Set to True to suppress data output to ALSA
+ */
+    void setMuted(bool);
+/**
+ * @brief This function checks whether this module is set to keyboard
+ * trigger mode.
+ *
+ * Its response depends on MidiSeq::restartByKbd and (TODO) whether there are notes
+ * pressed on the keyboard, i.e. whether the note was played stakato.
+ *
+ * @return True if the module accepts to be triggered
+ */
+    bool wantTrigByKbd();
+/**
+ * @brief This function does the actions related to a newly received note.
+ *
+ * It is called by SeqDriver when a new note is received on the ALSA input port.
+
+ * @param note The note value of the received note
+ * @param velocity The note velocity
+ * @param tick The time the note was received in internal ticks
+ */
+    void handleNote(int note, int velocity, int tick);
+/*! @brief This function sets the (controller) value of one point of the
+ * MidiSeq::customWave array. It is used for handling drawing functionality.
+ *
+ * The member is called by SeqWidget::mouseMoved or SeqWidget::mousePressed.
+ * The normalized mouse coordinates are scaled to the waveform size and
+ * resolution and to the controller range (0 ... 127). The function
+ * interpolates potentially missing waveform points between two events
+ * if the mouse buttons were not released.
+ *
+ * @param mouseX Normalized horizontal location of the mouse on the
+ * SeqScreen (0.0 ... 1.0)
+ * @param mouseY Normalized verical location of the mouse on the
+ * SeqScreen (0.0 ... 1.0)
+ * @see MidiSeq::toggleMutePoint(), MidiSeq::setMutePoint()
+ */
+    void setCustomWavePoint(double mouseX, double mouseY);
+/*! @brief This function sets the mute state of one point of the
+ * MidiSeq::muteMask array to the given state.
+ *
+ * The member is called when the right mouse button is clicked on the
+ * SeqScreen.
+ * If calculated waveforms are active, only the MidiSeq::muteMask is
+ * changed. If a custom waveform is active, the Sample.mute status
+ * at the given position is changed as well.
+ *
+ * @param mouseX Normalized horizontal location of the mouse on the
+ * SeqScreen (0.0 ... 1.0)
+ * @param muted mute state to set for the given position
+ *
+ * @see MidiSeq::toggleMutePoint()
+ */
+    void setMutePoint(double mouseX, bool muted);
+/*! @brief This function recalculates the MidiSeq::customWave as a
+ * function of the current MidiSeq::res and MidiSeq::size values.
+ *
+ * It is called upon every change of MidiSeq::size and MidiSeq::res. It
+ * repeats the current MidiSeq::customWave periodically if the new values
+ * lead to a bigger size data array.
+ */
     void resizeAll();
+/*! @brief Currently not in use. This function will copy the current
+ * MidiSeq::data array into MidiSeq::customWave.
+ */
     void copyToCustom();
+    void setRecordMode(int on);
     void setRecordedNote(int note);
+
+/**
+ * @brief This function checks whether an ALSA event is eligible for this
+ * module.
+ *
+ * Its response depends on the input filter settings, i.e. note,
+ * velocity and channel.
+ *
+ * @param inEv MidiEvent event to check
+ * @return True if inEv is in the input range of the module
+ */
+    bool wantEvent(MidiEvent inEv);
+/*! @brief This function is called upon every change of parameters in
+ * SeqWidget or upon input by mouse clicks on the SeqScreen.
+ *
+ * It fills the
+ * MidiSeq::data buffer with Sample points, which it copies from the
+ * MidiSeq::customWave data.
+ *
+ * @param data reference to an array the waveform is copied to
+ */
+    void getData(QVector<Sample> *data);
+/*! @brief This function transfers one Sample of data taken from
+ * the currently active waveform MidiLfo::data at the index frameptr.
+ *
+ * @param p_sample reference to a Sample structure receiving the data point
+ */
+    void getNextNote(Sample *p_sample);
+/*! @brief This function toggles the mute state of one point of the
+ * MidiSeq::muteMask array.
+ *
+ * The member is called when the right mouse button is clicked on the
+ * SeqScreen. The Sample.mute status at the given position in
+ * MidiSeq::customWave is changed as well.
+ *
+ * @param mouseX Normalized Horizontal location of the mouse on the
+ * SeqScreen (0.0 ... 1.0)
+ * @see MidiSeq::setMutePoint
+ */
+    bool toggleMutePoint(double);
+    void setCurrentIndex(int ix);
+
+  signals:
+    void nextStep(int currentIndex);
+    void noteEvent(int note, int velocity);
+
 };
-                              
+
 #endif
diff --git a/src/passwidget.cpp b/src/passwidget.cpp
index 68aaad3..2a4e7f2 100644
--- a/src/passwidget.cpp
+++ b/src/passwidget.cpp
@@ -1,3 +1,27 @@
+/*!
+ * @file passwidget.cpp
+ * @brief Implements the PassWidget UI class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #include <QBoxLayout>
 #include <QDialogButtonBox>
 #include <QLabel>
@@ -5,24 +29,25 @@
 #include "mainwindow.h"
 #include "passwidget.h"
 
-PassWidget::PassWidget(ArpData *p_arpData, int p_portcount, QWidget *parent)
+PassWidget::PassWidget(Engine *p_engine, int p_portcount, QWidget *parent)
             : QDialog(parent)
 {
-    arpData = p_arpData;
-    
+    int l1;
+
+    engine = p_engine;
+
     forwardCheck = new QCheckBox(this);
     forwardCheck->setText(tr("&Forward unmatched events to port"));
     forwardCheck->setChecked(false);
     QObject::connect(forwardCheck, SIGNAL(toggled(bool)), this,
             SLOT(updateForward(bool)));
 
-    portUnmatchedSpin = new QSpinBox(this);
+    portUnmatchedSpin = new QComboBox(this);
     portUnmatchedSpin->setDisabled(true);
-    portUnmatchedSpin->setRange(1, p_portcount);
-    portUnmatchedSpin->setKeyboardTracking(false);
-    QObject::connect(portUnmatchedSpin, SIGNAL(valueChanged(int)), this,
+    for (l1 = 0; l1 < p_portcount; l1++) portUnmatchedSpin->addItem(QString::number(l1 + 1));
+    QObject::connect(portUnmatchedSpin, SIGNAL(activated(int)), this,
             SLOT(updatePortUnmatched(int)));
-            
+
     QHBoxLayout *portBoxLayout = new QHBoxLayout;
     portBoxLayout->addWidget(forwardCheck);
     portBoxLayout->addStretch(1);
@@ -33,17 +58,17 @@ PassWidget::PassWidget(ArpData *p_arpData, int p_portcount, QWidget *parent)
     cbuttonCheck->setChecked(true);
     QObject::connect(cbuttonCheck, SIGNAL(toggled(bool)), this,
             SLOT(updateControlSetting(bool)));
-        
+
     compactStyleCheck = new QCheckBox(this);
     compactStyleCheck->setText(tr("&Compact module layout style"));
     QObject::connect(compactStyleCheck, SIGNAL(toggled(bool)), this,
             SLOT(updateCompactStyle(bool)));
     compactStyle = false;
-        
+
     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
 
     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
-    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));    
+    connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
 
     QVBoxLayout *passWidgetLayout = new QVBoxLayout;
     passWidgetLayout->addLayout(portBoxLayout);
@@ -63,13 +88,13 @@ PassWidget::~PassWidget()
 
 void PassWidget::updateForward(bool on)
 {
-    arpData->seqDriver->setForwardUnmatched(on);
+    engine->seqDriver->setForwardUnmatched(on);
     portUnmatchedSpin->setDisabled(!on);
 }
 
 void PassWidget::updatePortUnmatched(int id)
 {
-    arpData->seqDriver->setPortUnmatched(id - 1);
+    engine->seqDriver->setPortUnmatched(id);
 }
 
 void PassWidget::setForward(bool on)
@@ -79,19 +104,17 @@ void PassWidget::setForward(bool on)
 
 void PassWidget::setPortUnmatched(int id)
 {
-    portUnmatchedSpin->setValue(id);
+    portUnmatchedSpin->setCurrentIndex(id);
+    updatePortUnmatched(id);
 }
 
 void PassWidget::updateControlSetting(bool on)
 {
-    arpData->seqDriver->setMidiControllable(on);
+    engine->seqDriver->setMidiControllable(on);
 }
 
 void PassWidget::updateCompactStyle(bool on)
 {
     compactStyle = on;
-    arpData->setCompactStyle(on);
+    engine->setCompactStyle(on);
 }
-
-
-
diff --git a/src/passwidget.h b/src/passwidget.h
index 185aaeb..abd38e4 100644
--- a/src/passwidget.h
+++ b/src/passwidget.h
@@ -1,38 +1,69 @@
+/*!
+ * @file passwidget.h
+ * @brief Headers for the PassWidget UI class.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef PASSWIDGET_H
 #define PASSWIDGET_H
 
-#include <QCheckBox>
+#include <QComboBox>
 #include <QDialog>
 #include <QSpinBox>
 
-#include "arpdata.h"
+#include "engine.h"
 
+/*!
+ * The PassWidget class is a small QDialog UI that allows defining some
+ * global settings for QMidiArp. It is instantiated by MainWindow.
+ * It is shown when the MainWindow::viewSettingsAction() is triggered.
+
+ * @brief Preferences QDialog UI class. Instantiated by MainWindow.
+ */
 class PassWidget : public QDialog
 
 {
   Q_OBJECT
 
   private:
-    ArpData *arpData;
+    Engine *engine;
     QCheckBox *forwardCheck;
-    QSpinBox *portUnmatchedSpin;
-          
+    QComboBox *portUnmatchedSpin;
+
   public:
-    PassWidget(ArpData* arpData, int p_portcount, QWidget* parent=0);
+    PassWidget(Engine* engine, int p_portcount, QWidget* parent=0);
     ~PassWidget();
     void setForward(bool on);
     void setPortUnmatched(int id);
     QCheckBox *cbuttonCheck, *compactStyleCheck;
     bool compactStyle;
-    
+
   signals:
     void compactLayoutToggle(bool);
-        
+
   public slots:
     void updateForward(bool on);
     void updatePortUnmatched(int);
-    void updateControlSetting(bool);   
-    void updateCompactStyle(bool);   
+    void updateControlSetting(bool);
+    void updateCompactStyle(bool);
 };
-  
+
 #endif
diff --git a/src/pixmaps/Makefile.in b/src/pixmaps/Makefile.in
index 9b0e6f2..d58e016 100644
--- a/src/pixmaps/Makefile.in
+++ b/src/pixmaps/Makefile.in
@@ -36,7 +36,8 @@ POST_UNINSTALL = :
 subdir = src/pixmaps
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -63,6 +64,31 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
+DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@
+DX_CONFIG = @DX_CONFIG@
+DX_DOCDIR = @DX_DOCDIR@
+DX_DOT = @DX_DOT@
+DX_DOXYGEN = @DX_DOXYGEN@
+DX_DVIPS = @DX_DVIPS@
+DX_EGREP = @DX_EGREP@
+DX_ENV = @DX_ENV@
+DX_FLAG_DX_CURRENT_FEATURE = @DX_FLAG_DX_CURRENT_FEATURE@
+DX_FLAG_chi = @DX_FLAG_chi@
+DX_FLAG_chm = @DX_FLAG_chm@
+DX_FLAG_doc = @DX_FLAG_doc@
+DX_FLAG_dot = @DX_FLAG_dot@
+DX_FLAG_html = @DX_FLAG_html@
+DX_FLAG_man = @DX_FLAG_man@
+DX_FLAG_pdf = @DX_FLAG_pdf@
+DX_FLAG_ps = @DX_FLAG_ps@
+DX_FLAG_rtf = @DX_FLAG_rtf@
+DX_FLAG_xml = @DX_FLAG_xml@
+DX_HHC = @DX_HHC@
+DX_LATEX = @DX_LATEX@
+DX_MAKEINDEX = @DX_MAKEINDEX@
+DX_PDFLATEX = @DX_PDFLATEX@
+DX_PERL = @DX_PERL@
+DX_PROJECT = @DX_PROJECT@
 ECHO_C = @ECHO_C@
 ECHO_N = @ECHO_N@
 ECHO_T = @ECHO_T@
diff --git a/src/seqdriver.cpp b/src/seqdriver.cpp
index e2930f4..74e1a85 100644
--- a/src/seqdriver.cpp
+++ b/src/seqdriver.cpp
@@ -1,3 +1,27 @@
+/*!
+ * @file seqdriver.cpp
+ * @brief Implementation of the SeqDriver class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #include <cstdio>
 #include <QString>
 #include <alsa/asoundlib.h>
@@ -6,115 +30,94 @@
 #include "config.h"
 
 
-SeqDriver::SeqDriver(QList<MidiArp *> *p_midiArpList, 
+SeqDriver::SeqDriver(QList<MidiArp *> *p_midiArpList,
         QList<MidiLfo *> *p_midiLfoList, QList<MidiSeq *> *p_midiSeqList,
-        QWidget *parent)
+        int p_portCount, QWidget *parent)
     : QThread(parent), modified(false)
 {
     int err;
+    char buf[16];
+    int l1;
 
+    /** Register ALSA client */
     err = snd_seq_open(&seq_handle, "hw", SND_SEQ_OPEN_DUPLEX, 0);
     if (err < 0) {
         qWarning("Error opening ALSA sequencer (%s).", snd_strerror(err));
-        exit(1);  }
-        snd_seq_set_client_name(seq_handle, PACKAGE);
-        clientid = snd_seq_client_id(seq_handle);
-        portid_in = snd_seq_create_simple_port(seq_handle, "in",
-                        SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
-                        SND_SEQ_PORT_TYPE_APPLICATION);
-        if (portid_in < 0) {
+        exit(1);
+    }
+
+    snd_seq_set_client_name(seq_handle, PACKAGE);
+    clientid = snd_seq_client_id(seq_handle);
+
+    /** Register ALSA input port */
+    portid_in = snd_seq_create_simple_port(seq_handle, "in",
+                    SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
+                    SND_SEQ_PORT_TYPE_APPLICATION);
+    if (portid_in < 0) {
+        qWarning("Error creating sequencer port (%s).",
+                snd_strerror(portid_in));
+        exit(1);
+    }
+
+    /** Setup ALSA sequencer queue */
+    snd_seq_set_client_pool_output(seq_handle, SEQPOOL);
+    queue_id = snd_seq_alloc_queue(seq_handle);
+
+    /** Register ALSA output ports */
+    portCount = p_portCount;
+    for (l1 = 0; l1 < portCount; l1++) {
+        snprintf(buf, sizeof(buf), "out %d", l1 + 1);
+        portid_out[l1] = snd_seq_create_simple_port(seq_handle, buf,
+                SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
+                SND_SEQ_PORT_TYPE_APPLICATION);
+        if (portid_out[l1] < 0) {
             qWarning("Error creating sequencer port (%s).",
-                    snd_strerror(portid_in));
+                    snd_strerror(portid_out[l1]));
             exit(1);
         }
-    snd_seq_set_client_pool_output(seq_handle, SEQPOOL);
-                                        
-    initArpQueue();
+    }
 
     midiArpList = p_midiArpList;
     midiLfoList = p_midiLfoList;
     midiSeqList = p_midiSeqList;
-    
-    portCount = 0;
+
     portUnmatched = 0;
- 
-    grooveTick = 0;
-    grooveVelocity = 0;
-    grooveLength = 0;
-    
     forwardUnmatched = false;
+
     runArp = false;
     startQueue = false;
-    runQueueIfArp = true;
     use_jacksync = false;
     use_midiclock = false;
     midi_controllable = true;
-    tempo = 100;
+
     internal_tempo = 100;
     schedDelayTicks = 2;
-    m_ratio = 60e9/TICKS_PER_QUARTER/tempo;
-    int l1 = 0;
-    for (l1 = 0; l1 < 20; l1++) lastLfoTick[l1] = 0;
-    for (l1 = 0; l1 < 20; l1++) lastSeqTick[l1] = 0;
-    for (l1 = 0; l1 < 20; l1++) nextNoteTick[l1] = 0;
-    midiTick = 0;
-    nextLfoTick = 0;
-    nextSeqTick = 0;
-    nextEchoTick = 0;
-    lastKbdTick = 0;
-    gotKbdTrig = false;
+
+    resetTicks();
+
+    gotArpKbdTrig = false;
+    gotSeqKbdTrig = false;
     threadAbort = false;
+    start(Priority(6));
 }
 
 SeqDriver::~SeqDriver(){
-    
+
     if (use_jacksync) setUseJackTransport(false);
-    
+
     threadAbort = true;
     wait();
 
 }
 
-void SeqDriver::registerPorts(int num)
-{
-    int l1;
-    char buf[16];
-
-    portCount = num;
-    for (l1 = 0; l1 < portCount; l1++) {
-        snprintf(buf, sizeof(buf), "out %d", l1 + 1);
-        portid_out[l1] = snd_seq_create_simple_port(seq_handle, buf,
-                SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
-                SND_SEQ_PORT_TYPE_APPLICATION);
-        if (portid_out[l1] < 0) {
-            qWarning("Error creating sequencer port (%s).",
-                    snd_strerror(portid_out[l1]));
-            exit(1);
-        }
-    }                                    
-    start(Priority(6));
-}
-
-int SeqDriver::getPortCount()
-{
-    return(portCount);
-}
-
 void SeqDriver::run()
 {
-    int l1, l2;
-    QVector<int> note, velocity;
-    int noteTick = 0;
-    int length, ccnumber;
-    int lfoccnumber, lfochannel, lfoport;
-    int seqlength, seqchannel, seqport, seqtransp, seqvel;
-    snd_seq_event_t *evIn, evOut;
-    bool unmatched, foundEcho, isNew;
+    snd_seq_event_t *evIn;
+    bool unmatched;
     bool fallback = false;
+    MidiEvent inEv;
     int pollR = 0;
-    note.clear();
-    velocity.clear();
-    
+
     int nfds;
     struct pollfd *pfds;
 
@@ -123,319 +126,408 @@ void SeqDriver::run()
     snd_seq_poll_descriptors(seq_handle, pfds, nfds, POLLIN);
 
     while ((poll >= 0) && (!threadAbort)) {
-                
+
         pollR = poll(pfds, nfds, 200);
         while (pollR > 0) {
-    
+
             snd_seq_event_input(seq_handle, &evIn);
-    
-            if (use_midiclock && (evIn->type == SND_SEQ_EVENT_CLOCK)) {
+
+            inEv.type = evIn->type;
+            inEv.data = evIn->data.note.note;
+
+            if ((inEv.type == EV_CLOCK)&& use_midiclock) {
                 midiTick++;
-                tick = midiTick*TICKS_PER_QUARTER/MIDICLK_TPQ;
-                if (((int)tick > nextLfoTick) && (midiLfoList->count())) {
-                    fallback = true; 
+                tick = midiTick*TPQN/MIDICLK_TPQN;
+                if ((tick > nextMinLfoTick) && (midiLfoList->count())) {
+                    fallback = true;
                 }
-                if (((int)tick > nextSeqTick) && (midiSeqList->count())) {
-                    fallback = true; 
+                if ((tick > nextMinSeqTick) && (midiSeqList->count())) {
+                    fallback = true;
                 }
             }
-    
-            if (runArp && ((evIn->type == SND_SEQ_EVENT_ECHO) || startQueue || fallback)) {
-                
+
+            if (((inEv.type == EV_ECHO) || startQueue || fallback) && runArp) {
                 fallback = false;
-                
                 real_time = evIn->time.time;
-                
-                if (use_midiclock) {
-                    tick = midiTick*TICKS_PER_QUARTER/MIDICLK_TPQ;
-                    calcMidiRatio();
-                }
-                else if (use_jacksync) {
-                    if (jackSync->isRunning()) {
-                        
-                        if (!jackSync->get_state()) 
-                            setQueueStatus(false);
-        
-                        jpos = jackSync->get_pos();
-                        if (jpos.beats_per_minute > 0) 
-                            tempo = jpos.beats_per_minute;
-                            
-                        tick = (double)jpos.frame * TICKS_PER_QUARTER 
-                                / jpos.frame_rate * tempo / 60.
-                                - jack_offset_tick;
-                        calcMidiRatio();
+                handleEcho(inEv);
+            }
+            else {
+                unmatched = true;
+                inEv.channel = evIn->data.control.channel;
+
+                if ((inEv.type == EV_NOTEON) || (inEv.type == EV_NOTEOFF)) {
+                    real_time = evIn->time.time;
+                    inEv.data = evIn->data.note.note;
+                    if (inEv.type == EV_NOTEON) {
+                        inEv.value = evIn->data.note.velocity;
+                    }
+                    else {
+                        inEv.value = 0;
+                        inEv.type = EV_NOTEON;
                     }
                 }
-                else {
-                    m_ratio = 60e9/TICKS_PER_QUARTER/tempo;
-                    tick = deltaToTick(evIn->time.time);
+                else inEv.value = evIn->data.control.value;
+
+                if (inEv.type == EV_CONTROLLER) {
+                    inEv.data = evIn->data.control.param;
                 }
-                
-                
-                    //~ printf("       tick %d     \n",tick);
-                    //~ printf("nextLfoTick %d  ",nextLfoTick);
-                    //~ printf("nextSeqTick %d  ",nextSeqTick);
-                    //~ printf("nextEchoTick %d  \n",nextEchoTick);
-                    //~ printf("midiTick %d   ",midiTick);
-                    //~ printf("m_ratio %f  ",m_ratio);
-                    //~ printf("Jack Beat %d\n", jpos.beat);
-                    //~ printf("Jack Frame %d \n ", (int)jpos.frame);
-                    //~ printf("Jack BBT offset %d\n", (int)jpos.bbt_offset);
-                if (tick < 0) break;
-                startQueue = false;
-                foundEcho = false;
-    
-                //LFO data request and queueing
-                //add 8 ticks to startoff condition to cope with initial sync imperfections
-                if (((tick + 8) >= nextLfoTick) && (midiLfoList->count())) {
-                    l2 = 0;
-                    for (l1 = 0; l1 < midiLfoList->count(); l1++) {
-                        if ((tick + 8) >= (lastLfoTick[l1] + lfoPacketSize[l1])) {
-                            midiLfoList->at(l1)->getNextFrame(&lfoData);
-                            lfoccnumber = midiLfoList->at(l1)->ccnumber;
-                            lfochannel = midiLfoList->at(l1)->channelOut;
-                            lfoport = midiLfoList->at(l1)->portOut;
-                            if (!midiLfoList->at(l1)->isMuted) {
-                                l2 = 0;
-                                while (lfoData.at(l2).value > -1) {
-                                    if (!lfoData.at(l2).muted) {
-                                        snd_seq_ev_clear(&evOut);
-                                        snd_seq_ev_set_controller(&evOut, 0, 
-                                                lfoccnumber,
-                                                lfoData.at(l2).value);
-                                        evOut.data.control.channel = lfochannel;
-                                        snd_seq_ev_schedule_real(&evOut, queue_id, 0,
-                                                tickToDelta(lfoData.at(l2).tick + nextLfoTick));
-                                        snd_seq_ev_set_subs(&evOut);  
-                                        snd_seq_ev_set_source(&evOut,
-                                                portid_out[lfoport]);
-                                        snd_seq_event_output_direct(seq_handle, &evOut);
-                                    }
-                                    l2++;
-                                }
-                            }
-                            lastLfoTick[l1] += lfoPacketSize[l1];
-                            lfoPacketSize[l1] = lfoData.last().tick;
-                            if (!l1) 
-                                lfoMinPacketSize = lfoPacketSize[l1]; 
-                            else if (lfoPacketSize[l1] < lfoMinPacketSize) 
-                                lfoMinPacketSize = lfoPacketSize[l1];
-                        }
-                    }
-                    nextLfoTick += lfoMinPacketSize;
-    
-                    // next echo request for LFO
-                    snd_seq_ev_clear(evIn);
-                    evIn->type = SND_SEQ_EVENT_ECHO;
-                    snd_seq_ev_schedule_real(evIn, queue_id,  0,
-                            tickToDelta(nextLfoTick));
-                    snd_seq_ev_set_dest(evIn, clientid, portid_in);
+
+                unmatched = (handleEvent(inEv) && unmatched);
+
+                if (forwardUnmatched && unmatched) {
+                    snd_seq_ev_set_subs(evIn);
+                    snd_seq_ev_set_direct(evIn);
+                    snd_seq_ev_set_source(evIn, portid_out[portUnmatched]);
                     snd_seq_event_output_direct(seq_handle, evIn);
                 }
-                
-                //Seq notes data request and queueing
-                //add 8 ticks to startoff condition to cope with initial sync imperfections
-                if (((tick + 8) >= nextSeqTick) && (midiSeqList->count())) {
+
+                emit midiEvent(inEv.type, inEv.data, inEv.channel, inEv.value);
+            }
+            if (!runArp) tick = 0; //some events still come in after queue stop
+            pollR = snd_seq_event_input_pending(seq_handle, 0);
+        }
+    }
+}
+
+void SeqDriver::handleEcho(MidiEvent inEv)
+{
+    int l1, l2;
+    QVector<int> note, velocity;
+    int note_tick = 0;
+    int length;
+    int outport;
+    int seqtransp;
+    bool isNew;
+    MidiEvent outEv;
+    int frame_nticks = 0;
+
+    note.clear();
+    velocity.clear();
+
+    if (use_midiclock) {
+        tick = midiTick*TPQN/MIDICLK_TPQN;
+        calcClockRatio();
+    }
+    else if (use_jacksync) {
+        if (jackSync->isRunning()) {
+
+            if (!jackSync->get_state())
+                setQueueStatus(false);
+
+            jpos = jackSync->get_pos();
+            if (jpos.beats_per_minute > 0)
+                tempo = jpos.beats_per_minute;
+
+            tick = (double)jpos.frame * TPQN
+                    / jpos.frame_rate * tempo / 60.
+                    - jack_offset_tick;
+            calcClockRatio();
+        }
+    }
+    else {
+        m_ratio = 60e9/TPQN/tempo;
+        tick = deltaToTick(aTimeToDelta(&real_time));
+    }
+
+
+        //~ printf("       tick %d     ",tick);
+        //~ printf("nextMinLfoTick %d  ",nextMinLfoTick);
+        //~ printf("nextMinSeqTick %d  ",nextMinSeqTick);
+        //~ printf("nextMinArpTick %d  \n",nextMinArpTick);
+        //~ printf("midiTick %d   ",midiTick);
+        //~ printf("m_ratio %f  ",m_ratio);
+        //~ printf("Jack Beat %d\n", jpos.beat);
+        //~ printf("Jack Frame %d \n ", (int)jpos.frame);
+        //~ printf("Jack BBT offset %d\n", (int)jpos.bbt_offset);
+    if (tick < 0) return;
+    startQueue = false;
+
+    //LFO data request and queueing
+    //add 8 ticks to startoff condition to cope with initial sync imperfections
+    if (((tick + 8) >= nextMinLfoTick) && (midiLfoList->count())) {
+        for (l1 = 0; l1 < midiLfoList->count(); l1++) {
+            if ((tick + 8) >= nextLfoTick[l1]) {
+                outEv.type = EV_CONTROLLER;
+                outEv.data = midiLfoList->at(l1)->ccnumber;
+                outEv.channel = midiLfoList->at(l1)->channelOut;
+                midiLfoList->at(l1)->getNextFrame(&lfoData);
+                frame_nticks = lfoData.last().tick;
+                outport = midiLfoList->at(l1)->portOut;
+                if (!midiLfoList->at(l1)->isMuted) {
                     l2 = 0;
-                    for (l1 = 0; l1 < midiSeqList->count(); l1++) {
-                        if ((tick + 8) >= (lastSeqTick[l1] + seqPacketSize[l1])) {
-                            midiSeqList->at(l1)->getData(&seqData);
-                            seqlength = midiSeqList->at(l1)->notelength;
-                            seqtransp = midiSeqList->at(l1)->transp; 
-                            seqvel = midiSeqList->at(l1)->vel; 
-                            seqchannel = midiSeqList->at(l1)->channelOut;
-                            seqport = midiSeqList->at(l1)->portOut;
-                            if (!midiSeqList->at(l1)->isMuted) {
-                                l2 = 0;
-                                while (seqData.at(l2).value > -1) {
-                                    if (!seqData.at(l2).muted) {
-                                        snd_seq_ev_clear(&evOut);
-                                        snd_seq_ev_set_note(&evOut, 0, 
-                                                seqData.at(l2).value+seqtransp,
-                                                seqvel, seqlength);
-                                        evOut.data.control.channel = seqchannel;
-                                        snd_seq_ev_schedule_real(&evOut, queue_id, 0,
-                                                tickToDelta(seqData.at(l2).tick + nextSeqTick));
-                                        snd_seq_ev_set_subs(&evOut);  
-                                        snd_seq_ev_set_source(&evOut,
-                                                portid_out[seqport]);
-                                        snd_seq_event_output_direct(seq_handle, &evOut);
-                                    }
-                                    l2++;
-                                }
-                            }
-                            lastSeqTick[l1] += seqPacketSize[l1];
-                            seqPacketSize[l1] = seqData.last().tick;
-                            if (!l1) 
-                                seqMinPacketSize = seqPacketSize[l1]; 
-                            else if (seqPacketSize[l1] < seqMinPacketSize) 
-                                seqMinPacketSize = seqPacketSize[l1];
+                    while (lfoData.at(l2).value > -1) {
+                        if (!lfoData.at(l2).muted) {
+                            outEv.value = lfoData.at(l2).value;
+                            schedEvent(outEv, nextLfoTick[l1] + lfoData.at(l2).tick
+                                , outport);
                         }
-                    }
-                    nextSeqTick += seqMinPacketSize;
-    
-                    // next echo request for Seq
-                    if (((nextSeqTick != nextLfoTick) || (!nextLfoTick))) {
-                        snd_seq_ev_clear(evIn);
-                        evIn->type = SND_SEQ_EVENT_ECHO;
-                        snd_seq_ev_schedule_real(evIn, queue_id,  0,
-                                tickToDelta(nextSeqTick));
-                        snd_seq_ev_set_dest(evIn, clientid, portid_in);
-                        snd_seq_event_output_direct(seq_handle, evIn);
+                        l2++;
                     }
                 }
-                
-                //Arp Note queueing
-                if ((tick + 8) >= nextEchoTick) {
-                    for (l1 = 0; l1 < midiArpList->count(); l1++) {
-                        if (((evIn->data.note.note == 0) && gotKbdTrig
-                                && midiArpList->at(l1)->wantTrigByKbd()) 
-                                || (!gotKbdTrig && (evIn->data.note.note == 0))) {
-                            gotKbdTrig = false;
-                            if (tick + schedDelayTicks >= nextNoteTick[l1]) {
-                                midiArpList->at(l1)->newRandomValues();
-                                midiArpList->at(l1)->updateQueueTempo(tempo);
-                                midiArpList->at(l1)->getCurrentNote(tick);
-                                note = midiArpList->at(l1)->returnNote;
-                                nextNoteTick[l1] = midiArpList->at(l1)->getNextNoteTick();
-                                velocity = midiArpList->at(l1)->returnVelocity;
-                                noteTick = midiArpList->at(l1)->returnTick;
-                                length = midiArpList->at(l1)->returnLength;
-                                isNew = midiArpList->at(l1)->returnIsNew;
-                                if (!velocity.isEmpty()) {
-                                    if (isNew && velocity.at(0)) {
-                                        l2 = 0;
-                                        while(note.at(l2) >= 0) {
-                                            snd_seq_ev_clear(&evOut);
-                                            snd_seq_ev_set_note(&evOut, 0, note.at(l2),
-                                                    velocity.at(l2), (int)length*4);
-                                            evOut.data.control.channel = 
-                                                midiArpList->at(l1)->channelOut;
-                                            snd_seq_ev_schedule_real(&evOut, queue_id, 0,
-                                                    tickToDelta(noteTick));
-                                            snd_seq_ev_set_subs(&evOut);  
-                                            snd_seq_ev_set_source(&evOut,
-                                                    portid_out[midiArpList->at(l1)->portOut]);
-                                            snd_seq_event_output_direct(seq_handle, &evOut);
-                                            l2++;                   
-                                        } 
-                                    }
-                                }
-                            }
+                nextLfoTick[l1] += frame_nticks;
+                /** round-up to current resolution (quantize) */
+                nextLfoTick[l1]/= frame_nticks;
+                nextLfoTick[l1]*= frame_nticks;
+            }
+            if (!l1)
+                nextMinLfoTick = nextLfoTick[l1];
+            else if (nextLfoTick[l1] < nextMinLfoTick)
+                nextMinLfoTick = nextLfoTick[l1];
+        }
+        requestEchoAt(nextMinLfoTick);
+    }
+
+    //Seq notes data request and queueing
+    //add 8 ticks to startoff condition to cope with initial sync imperfections
+    if (((tick + 8) >= nextMinSeqTick) && (midiSeqList->count())) {
+        for (l1 = 0; l1 < midiSeqList->count(); l1++) {
+            if ((gotSeqKbdTrig && (inEv.data == 2) && midiSeqList->at(l1)->wantTrigByKbd())
+                    || (!gotSeqKbdTrig && (inEv.data == 0))) {
+                gotSeqKbdTrig = false;
+                if ((tick + 8) >= nextSeqTick[l1]) {
+                    outEv.type = EV_NOTEON;
+                    outEv.value = midiSeqList->at(l1)->vel;
+                    outEv.channel = midiSeqList->at(l1)->channelOut;
+                    midiSeqList->at(l1)->getNextNote(&seqSample);
+                    frame_nticks = TPQN / midiSeqList->at(l1)->res;
+                    length = midiSeqList->at(l1)->notelength;
+                    seqtransp = midiSeqList->at(l1)->transp;
+                    outport = midiSeqList->at(l1)->portOut;
+                    if (!midiSeqList->at(l1)->isMuted) {
+                        if (!seqSample.muted) {
+                            outEv.data = seqSample.value + seqtransp;
+                            schedEvent(outEv, nextSeqTick[l1], outport, length);
                         }
-                        if (!l1) 
-                            nextEchoTick = nextNoteTick[l1] - schedDelayTicks; 
-                        else if (nextNoteTick[l1] < nextEchoTick + schedDelayTicks) 
-                            nextEchoTick = nextNoteTick[l1] - schedDelayTicks;
                     }
-                    //next echo request for Arp
-                    if (0 > nextEchoTick) nextEchoTick = 0;
-                    if (((nextEchoTick != nextLfoTick) || (!nextLfoTick)) &&
-                     ((nextEchoTick != nextSeqTick) || (!nextSeqTick))) {
-                        snd_seq_ev_clear(evIn);
-                        evIn->type = SND_SEQ_EVENT_ECHO;
-                        evIn->data.note.note = 0;
-                        snd_seq_ev_schedule_real(evIn, queue_id,  0, 
-                                tickToDelta(nextEchoTick));
-                        snd_seq_ev_set_dest(evIn, clientid, portid_in);
-                        snd_seq_event_output_direct(seq_handle, evIn);
+                    nextSeqTick[l1]+=frame_nticks;
+                    if (!midiSeqList->at(l1)->trigByKbd) {
+                        /** round-up to current resolution (quantize) */
+                        nextSeqTick[l1]/=frame_nticks;
+                        nextSeqTick[l1]*=frame_nticks;
                     }
                 }
             }
-            else {
-                emit midiEvent(evIn);
-                unmatched = true;
-    
-                if (evIn->type == SND_SEQ_EVENT_CONTROLLER) {
-                    ccnumber = (int)evIn->data.control.param;
-                    if (ccnumber == 64) {
-                        //Sustain Footswitch has changed
-                        for (l1 = 0; l1 < midiArpList->count(); l1++) {
-                            if (midiArpList->at(l1)->isArp(evIn)) {
-                                midiArpList->at(l1)->setSustain(
-                                        (evIn->data.control.value == 127), tick);
-                                unmatched = false;
+            if (!l1)
+                nextMinSeqTick = nextSeqTick[l1];
+            else if (nextSeqTick[l1] < nextMinSeqTick)
+                nextMinSeqTick = nextSeqTick[l1];
+        }
+        requestEchoAt(nextMinSeqTick, 0);
+    }
+
+    //Arp Note queueing
+    if ((tick + 8) >= nextMinArpTick) {
+        for (l1 = 0; l1 < midiArpList->count(); l1++) {
+            if ((gotArpKbdTrig && (inEv.data == 2) && midiArpList->at(l1)->wantTrigByKbd())
+                    || (!gotArpKbdTrig && (inEv.data == 0))) {
+                gotArpKbdTrig = false;
+                if (tick + schedDelayTicks >= nextArpTick[l1]) {
+                    outEv.type = EV_NOTEON;
+                    outEv.channel = midiArpList->at(l1)->channelOut;
+                    midiArpList->at(l1)->newRandomValues();
+                    midiArpList->at(l1)->updateQueueTempo(tempo);
+                    midiArpList->at(l1)->prepareCurrentNote(tick);
+                    note = midiArpList->at(l1)->returnNote;
+                    velocity = midiArpList->at(l1)->returnVelocity;
+                    note_tick = midiArpList->at(l1)->returnTick;
+                    length = midiArpList->at(l1)->returnLength * 4;
+                    outport = midiArpList->at(l1)->portOut;
+                    isNew = midiArpList->at(l1)->returnIsNew;
+                    if (!velocity.isEmpty()) {
+                        if (isNew && velocity.at(0)) {
+                            l2 = 0;
+                            while(note.at(l2) >= 0) {
+                                outEv.data = note.at(l2);
+                                outEv.value = velocity.at(l2);
+                                schedEvent(outEv, note_tick, outport, length);
+                                l2++;
                             }
                         }
                     }
-                    else {
-                        if (midi_controllable) {
-                            emit controlEvent(ccnumber, evIn->data.control.channel,
-                                                evIn->data.control.value);
-                            unmatched = false;
-                        }
-                    }
+                    nextArpTick[l1] = midiArpList->at(l1)->getNextNoteTick();
                 }
-    
-                if ((evIn->type == SND_SEQ_EVENT_NOTEON)
-                        || (evIn->type == SND_SEQ_EVENT_NOTEOFF)) {
-                    get_time();
-                    tick = deltaToTick(tmptime);
-                    for (l1 = 0; l1 < midiSeqList->count(); l1++) {
-                        if (midiSeqList->at(l1)->isSeq(evIn)) {
-                            unmatched = false;
-                            if ((evIn->type == SND_SEQ_EVENT_NOTEON)
-                                    && (evIn->data.note.velocity > 0)) {
-                                emit noteEvent(evIn->data.note.note, 
-                                        evIn->data.note.velocity);
-                            } 
-                        }
-                    }
-                    for (l1 = 0; l1 < midiArpList->count(); l1++) {
-                        if (midiArpList->at(l1)->isArp(evIn)) {
-                            unmatched = false;
-                            if ((evIn->type == SND_SEQ_EVENT_NOTEON)
-                                    && (evIn->data.note.velocity > 0)) {
-                                
-                                midiArpList->at(l1)->addNote(evIn->data.note.note, 
-                                                evIn->data.note.velocity, tick);
-                                                
-                                if (midiArpList->at(l1)->wantTrigByKbd()) {
-                                    nextEchoTick = tick;
-                                    nextNoteTick[l1] = nextEchoTick + schedDelayTicks;
-                                    gotKbdTrig = true;
-                                }
-                            } 
-                            else {
-                                midiArpList->at(l1)->handleNoteOff(evIn->data.note.note, tick, 1);
-                            }
-                        }
-                    }
-                    // place echo request if in keyboard trigger mode
-                    if (gotKbdTrig 
-                     && ((nextEchoTick != nextLfoTick) || (!nextLfoTick))
-                     && ((nextEchoTick != nextSeqTick) || (!nextSeqTick))
-                     && (nextEchoTick != lastKbdTick)) {
-                        lastKbdTick = nextEchoTick;
-                        snd_seq_ev_clear(evIn);
-                        evIn->type = SND_SEQ_EVENT_ECHO;
-                        evIn->data.note.note = 0;
-                        snd_seq_ev_schedule_real(evIn, queue_id,  0, 
-                                tickToDelta(nextEchoTick));
-                        snd_seq_ev_set_dest(evIn, clientid, portid_in);
-                        snd_seq_event_output_direct(seq_handle, evIn);
-                    }
+            }
+            if (!l1)
+                nextMinArpTick = nextArpTick[l1] - schedDelayTicks;
+            else if (nextArpTick[l1] < nextMinArpTick + schedDelayTicks)
+                nextMinArpTick = nextArpTick[l1] - schedDelayTicks;
+        }
+
+        if (0 > nextMinArpTick) nextMinArpTick = 0;
+        requestEchoAt(nextMinArpTick, 0);
+    }
+}
+
+bool SeqDriver::handleEvent(MidiEvent inEv)
+{
+    bool unmatched;
+    int l1;
+    unmatched = true;
+
+    if (inEv.type == EV_CONTROLLER) {
+
+        if (inEv.data == CT_FOOTSW) {
+            for (l1 = 0; l1 < midiArpList->count(); l1++) {
+                if (midiArpList->at(l1)->wantEvent(inEv)) {
+                    midiArpList->at(l1)->setSustain((inEv.value == 127), tick);
+                    unmatched = false;
                 }
-                if (use_midiclock){
-                    if (evIn->type == SND_SEQ_EVENT_START) {
-                        setQueueStatus(true);
-                    }
-                    if (evIn->type == SND_SEQ_EVENT_STOP) {
-                        setQueueStatus(false);
+            }
+        }
+        else {
+            //Does any LFO want to record this?
+            for (l1 = 0; l1 < midiLfoList->count(); l1++) {
+                if (midiLfoList->at(l1)->wantEvent(inEv)) {
+                    midiLfoList->at(l1)->record(inEv.value);
+                    unmatched = false;
+                }
+            }
+            if (midi_controllable) {
+                emit controlEvent(inEv.data, inEv.channel, inEv.value);
+                unmatched = false;
+            }
+        }
+        return unmatched;
+    }
+
+    if (inEv.type == EV_NOTEON) {
+
+        for (l1 = 0; l1 < midiSeqList->count(); l1++) {
+            if (midiSeqList->at(l1)->wantEvent(inEv)) {
+                unmatched = false;
+
+                get_time();
+                tick = deltaToTick(aTimeToDelta(&tmptime));
+                midiSeqList->at(l1)->handleNote(inEv.data, inEv.value, tick);
+
+                if (inEv.value && midiSeqList->at(l1)->wantTrigByKbd()) {
+                    nextMinSeqTick = tick;
+                    nextSeqTick[l1] = nextMinSeqTick + schedDelayTicks;
+                    gotSeqKbdTrig = true;
+                    requestEchoAt(nextMinSeqTick, 2);
+                }
+            }
+        }
+        for (l1 = 0; l1 < midiArpList->count(); l1++) {
+            if (midiArpList->at(l1)->wantEvent(inEv)) {
+                unmatched = false;
+                if (inEv.value) {
+
+                    get_time();
+                    tick = deltaToTick(aTimeToDelta(&tmptime));
+                    midiArpList->at(l1)->handleNoteOn(inEv.data, inEv.value, tick);
+
+                    if (midiArpList->at(l1)->wantTrigByKbd()) {
+                        nextMinArpTick = tick;
+                        nextArpTick[l1] = nextMinArpTick + schedDelayTicks;
+                        gotArpKbdTrig = true;
+                        requestEchoAt(nextMinArpTick, 2);
                     }
                 }
-    
-                if (forwardUnmatched && unmatched) {
-                    snd_seq_ev_set_subs(evIn);  
-                    snd_seq_ev_set_direct(evIn);
-                    snd_seq_ev_set_source(evIn, portid_out[portUnmatched]);
-                    snd_seq_event_output_direct(seq_handle, evIn);
+                else {
+                    midiArpList->at(l1)->handleNoteOff(inEv.data, tick, 1);
                 }
             }
-            if (!runArp) tick = 0; //some events still come in after queue stop 
-            pollR = snd_seq_event_input_pending(seq_handle, 0);
         }
+        return unmatched;
+    }
+
+    if (use_midiclock){
+        if (inEv.type == EV_START) {
+            setQueueStatus(true);
+        }
+        if (inEv.type == EV_STOP) {
+            setQueueStatus(false);
+        }
+        return unmatched;
     }
+    return unmatched;
+}
+
+void SeqDriver::resetTicks()
+{
+    int l1;
+
+    for (l1 = 0; l1 < midiArpList->count(); l1++) {
+        midiArpList->at(l1)->foldReleaseTicks(tick);
+        midiArpList->at(l1)->initArpTick(0);
+    }
+    for (l1 = 0; l1 < midiLfoList->count(); l1++) {
+        midiLfoList->at(l1)->resetFramePtr();
+    }
+    for (l1 = 0; l1 < midiSeqList->count(); l1++) {
+        midiSeqList->at(l1)->setCurrentIndex(0);
+    }
+    for (l1 = 0; l1 < 20; l1++) {
+        nextArpTick[l1] = 0;
+        nextLfoTick[l1] = 0;
+        nextSeqTick[l1] = 0;
+    }
+    nextMinLfoTick = 0;
+    nextMinSeqTick = 0;
+    nextMinArpTick = 0;
+    lastSchedTick = 0;
+    jack_offset_tick = 0;
+
+    if (use_midiclock) {
+        midiTick = 0;
+    }
+    else if (use_jacksync) {
+        if (jackSync->isRunning()) {
+            jpos = jackSync->get_pos();
+            // qtractor for instance doesn't set tempo atm...
+            if (jpos.beats_per_minute > 0)
+                tempo = jpos.beats_per_minute;
+            else
+                tempo = internal_tempo;
+            jack_offset_tick = (double)jpos.frame * TPQN
+                    / jpos.frame_rate * tempo / 60;
+            m_ratio = 60e9/TPQN/tempo;
+        }
+    }
+    else {
+        tempo = internal_tempo;
+        m_ratio = 60e9/TPQN/tempo;
+    }
+
+    tick = 0;
+}
+
+void SeqDriver::schedEvent(MidiEvent outEv, int n_tick, int outport, int length)
+{
+    snd_seq_event_t ev;
+    snd_seq_ev_clear(&ev);
+
+    ev.type = outEv.type;
+    if (ev.type == EV_NOTEON) {
+        snd_seq_ev_set_note(&ev, 0, outEv.data, outEv.value, length);
+    }
+    else if (ev.type == EV_CONTROLLER) {
+        ev.data.control.param = outEv.data;
+        ev.data.control.value = outEv.value;
+    }
+
+    ev.data.control.channel = outEv.channel;
+    snd_seq_ev_schedule_real(&ev, queue_id, 0, deltaToATime(tickToDelta(n_tick)));
+    snd_seq_ev_set_subs(&ev);
+    snd_seq_ev_set_source(&ev, portid_out[outport]);
+    snd_seq_event_output_direct(seq_handle, &ev);
+}
+
+bool SeqDriver::requestEchoAt(int echoTick, int infotag)
+{
+    if ((echoTick == lastSchedTick) && (echoTick)) return false;
+
+    lastSchedTick = echoTick;
+
+    snd_seq_event_t ev;
+    snd_seq_ev_clear(&ev);
+    ev.type = SND_SEQ_EVENT_ECHO;
+    ev.data.note.note = infotag;
+    snd_seq_ev_schedule_real(&ev, queue_id,  0, deltaToATime(tickToDelta(echoTick)));
+    snd_seq_ev_set_dest(&ev, clientid, portid_in);
+    snd_seq_event_output_direct(seq_handle, &ev);
+    return true;
 }
 
 void SeqDriver::setForwardUnmatched(bool on)
@@ -450,16 +542,11 @@ void SeqDriver::setPortUnmatched(int id)
     modified = true;
 }
 
-void SeqDriver::initArpQueue()
-{
-    queue_id = snd_seq_alloc_queue(seq_handle);
-}
-
 void SeqDriver::setQueueTempo(int bpm)
 {
     tempo = bpm;
     internal_tempo = bpm;
-    m_ratio = 60e9/TICKS_PER_QUARTER/tempo;
+    m_ratio = 60e9/TPQN/tempo;
 }
 
 void SeqDriver::get_time()
@@ -469,87 +556,32 @@ void SeqDriver::get_time()
     snd_seq_queue_status_malloc(&status);
     snd_seq_get_queue_status(seq_handle, queue_id, status);
 
-    const snd_seq_real_time_t* current_time = 
+    const snd_seq_real_time_t* current_time =
         snd_seq_queue_status_get_real_time(status);
     tmptime = *current_time;
     snd_seq_queue_status_free(status);
 }
 
-void SeqDriver::runQueue(bool on)
-{
-    runQueueIfArp = on;
-    setQueueStatus(on);
-}
-
 void SeqDriver::setQueueStatus(bool run)
 {
-    int l1;
-    snd_seq_event_t evOut;
-
     if (run) {
-        for (l1 = 0; l1 < midiArpList->count(); l1++) {
-            midiArpList->at(l1)->foldReleaseTicks(tick);
-        }
-        for (l1 = 0; l1 < midiLfoList->count(); l1++) {
-            midiLfoList->at(l1)->resetFramePtr();
-        }
+
         runArp = true;
         startQueue = true;
+
+        resetTicks();
+
         snd_seq_start_queue(seq_handle, queue_id, NULL);
-        snd_seq_drain_output(seq_handle);   
-        snd_seq_ev_clear(&evOut);
-        for (l1 = 0; l1 < 20; l1++) {
-            nextNoteTick[l1] = 0;
-            lastLfoTick[l1] = 0;
-            lfoPacketSize[l1] = 0;
-            lastSeqTick[l1] = 0;
-            seqPacketSize[l1] = 0;
-        }
-        nextLfoTick = 0;
-        nextSeqTick = 0;
-        nextEchoTick = 0;
-        jack_offset_tick = 0;
-          
-        if (use_midiclock) {
-            midiTick = 0;
-        }
-        else if (use_jacksync) {
-            if (jackSync->isRunning()) {
-                jpos = jackSync->get_pos();
-                // qtractor for instance doesn't set tempo atm...
-                if (jpos.beats_per_minute > 0) 
-                    tempo = jpos.beats_per_minute;
-                else
-                    tempo = internal_tempo;
-                jack_offset_tick = (double)jpos.frame * TICKS_PER_QUARTER 
-                        / jpos.frame_rate * tempo / 60;
-                m_ratio = 60e9/TICKS_PER_QUARTER/tempo;
-            }
-        }
-        else {
-            tempo = internal_tempo;
-            m_ratio = 60e9/TICKS_PER_QUARTER/tempo;
-        }
+        snd_seq_drain_output(seq_handle);
 
-        tick = 0;
-        evOut.type = SND_SEQ_EVENT_NOTEOFF;
-        evOut.data.note.note = 0;
-        evOut.data.note.velocity = 0;
-        snd_seq_ev_schedule_real(&evOut, queue_id,  0, tickToDelta(0));
-        snd_seq_ev_set_dest(&evOut, clientid, portid_in);
-        snd_seq_event_output_direct(seq_handle, &evOut);
-        
-        for (l1 = 0; l1 < midiArpList->count(); l1++) {
-            midiArpList->at(l1)->initArpTick(tick);
-        }
-        
-        qWarning("Alsa Queue started");
+        requestEchoAt(0);
 
+        qWarning("Alsa Queue started");
     }
     else {
         runArp = false;
         //    snd_seq_drop_output(seq_handle);
-        for (l1 = 0; l1 < midiArpList->count(); l1++) {
+        for (int l1 = 0; l1 < midiArpList->count(); l1++) {
             midiArpList->at(l1)->clearNoteBuffer();
         }
         snd_seq_remove_events_t *remove_ev;
@@ -569,63 +601,17 @@ void SeqDriver::setQueueStatus(bool run)
     }
 }
 
-void SeqDriver::setGrooveTick(int val)
-{
-    int l1;
-
-    grooveTick = val;
-    for (l1 = 0; l1 < midiArpList->count(); l1++) {
-        midiArpList->at(l1)->newGrooveValues(grooveTick, grooveVelocity,
-                grooveLength);
-    }
-    modified = true;
-}
-
-void SeqDriver::setGrooveVelocity(int val)
-{
-    int l1;
-
-    grooveVelocity = val;
-    for (l1 = 0; l1 < midiArpList->count(); l1++) {
-        midiArpList->at(l1)->newGrooveValues(grooveTick, grooveVelocity,
-                grooveLength);
-    }
-    modified = true;
-}
-
-void SeqDriver::setGrooveLength(int val)
-{
-    int l1;
-
-    grooveLength = val;
-    for (l1 = 0; l1 < midiArpList->count(); l1++) {
-        midiArpList->at(l1)->newGrooveValues(grooveTick, grooveVelocity,
-                grooveLength);
-    }
-    modified = true;
-}
-
-void SeqDriver::sendGroove()
-{
-    int l1;
-
-    for (l1 = 0; l1 < midiArpList->count(); l1++) {
-        midiArpList->at(l1)->newGrooveValues(grooveTick, grooveVelocity,
-                grooveLength);
-    }
-}
-
 void SeqDriver::setUseJackTransport(bool on)
 {
     if (on) {
         jackSync = new JackSync();
         jackSync->setParent(this);
-        connect(jackSync, SIGNAL(j_tr_state(bool)), 
-                this, SLOT(runQueue(bool)));
-        connect(jackSync, SIGNAL(j_shutdown()), 
+        connect(jackSync, SIGNAL(j_tr_state(bool)),
+                this, SLOT(setQueueStatus(bool)));
+        connect(jackSync, SIGNAL(j_shutdown()),
                 this, SLOT(jackShutdown()));
         use_jacksync = true;
-                
+
         if (jackSync->initJack()) {
             emit jackShutdown(false);
             return;
@@ -642,7 +628,7 @@ void SeqDriver::setUseJackTransport(bool on)
             use_jacksync = false;
         }
     }
-}               
+}
 
 void SeqDriver::jackShutdown()
 {
@@ -652,8 +638,8 @@ void SeqDriver::jackShutdown()
 
 void SeqDriver::setUseMidiClock(bool on)
 {
-    m_ratio = 60e9/TICKS_PER_QUARTER/tempo;
-    runQueue(false);
+    m_ratio = 60e9/TPQN/tempo;
+    setQueueStatus(false);
     use_midiclock = on;
 }
 
@@ -667,29 +653,34 @@ bool SeqDriver::isModified()
     return modified;
 }
 
-const snd_seq_real_time_t* SeqDriver::tickToDelta(int tick)
+double SeqDriver::tickToDelta(int tick)
 {
-    double tmp =  m_ratio * tick;
-    delta.tv_sec = (int)(tmp * 1e-9);
-    delta.tv_nsec = (long)(tmp - (double)delta.tv_sec * 1e9);
-    return δ
+    return (double)m_ratio * tick;
 }
 
-int SeqDriver::deltaToTick(snd_seq_real_time_t curtime)
+int SeqDriver::deltaToTick(double curtime)
 {
-    double alsatick;
-    alsatick = (double)curtime.tv_sec * 1e9 / m_ratio
-        + (double)curtime.tv_nsec / m_ratio;
-    return (int)(alsatick + .5);
+    return (int)(curtime / m_ratio + .5);
 }
 
-void SeqDriver::calcMidiRatio()
+double SeqDriver::aTimeToDelta(snd_seq_real_time_t* atime)
+{
+    return (double)atime->tv_sec * 1e9 + (double)atime->tv_nsec;
+}
+
+const snd_seq_real_time_t* SeqDriver::deltaToATime(double curtime)
+{
+    delta.tv_sec = (int)(curtime * 1e-9);
+    delta.tv_nsec = (long)(curtime - (double)delta.tv_sec * 1e9);
+    return δ
+}
+
+void SeqDriver::calcClockRatio()
 {
     double old_m_ratio = m_ratio;
-    
+
     if (tick > 0) {
-        m_ratio = ((double)real_time.tv_sec * 1e9 
-                + (double)real_time.tv_nsec)/tick;
+        m_ratio = aTimeToDelta(&real_time)/tick;
     }
     if ((m_ratio == 0) || (m_ratio > 60e9 / tempo)) {
         m_ratio = old_m_ratio;
diff --git a/src/seqdriver.h b/src/seqdriver.h
index 4b6dda7..c456e66 100644
--- a/src/seqdriver.h
+++ b/src/seqdriver.h
@@ -1,3 +1,27 @@
+/*!
+ * @file seqdriver.h
+ * @brief Header for the SeqDriver class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef SEQDRIVER_H
 #define SEQDRIVER_H
 
@@ -11,87 +35,114 @@
 #include "midiseq.h"
 #include "main.h"
 
-
+/*! @brief ALSA sequencer backend QThread class. Also creates JackSync
+ *
+ * SeqDriver is created by Engine at the moment of program start. Its
+ * constructor registers ALSA seq input port and the requested number of
+ * output ports. I also creates a JackSync instance whose ports are only
+ * created when the SeqDriver::setUseJackTransport member is called.
+ * Pointers to the MIDI workers MidiLfo, MidiSeq are passed to SeqDriver
+ * as arguments.
+ * The SeqDriver::run() thread is the ALSA sequencer "callback" process
+ * handling all incoming and outgoing sequencer events.
+ * When the SeqDriver::setQueueStatus() member is called with True argument,
+ * a so called "echo event" is scheduled with zero time. Echo events go back
+ * to the callback process and allow output and reception of sequencer
+ * events depending on the ALSA queue timing. Depending on the event types,
+ * the MIDI worker interfaces are called in series and return their
+ * data to be output to the queue. After the data output, a new echo
+ * event is requested for the next MIDI event to be output, which will
+ * again call the SeqDriver::run() thread, and so on.
+ * In order to provide accurate synchronization with external sources
+ * such as Jack Transport or an incoming ALSA MIDI clock,
+ * SeqDriver works with snd_seq_real_time timing information when it
+ * communicates with the ALSA queue. Internally, the real time information
+ * is rescaled to a simpler tick-based timing, which is currently 192 tpqn
+ * using the SeqDriver::deltaToTick and SeqDriver::tickToDelta functions.
+ */
 class SeqDriver : public QThread {
 
     Q_OBJECT
-    
+
     private:
         int portCount;
-        QList<MidiArp *> *midiArpList; 
-        QList<MidiLfo *> *midiLfoList; 
-        QList<MidiSeq *> *midiSeqList; 
+        QList<MidiArp *> *midiArpList;
+        QList<MidiLfo *> *midiLfoList;
+        QList<MidiSeq *> *midiSeqList;
         snd_seq_t *seq_handle;
         int clientid;
         int portid_out[MAX_PORTS];
-        int lfoMinPacketSize, lfoPacketSize[20];
-        int seqMinPacketSize, seqPacketSize[20];
+        int lfoMinPacketSize;
+        int seqMinPacketSize;
         int portid_in;
         int queue_id;
         bool startQueue;
         bool modified;
         bool midi_controllable;
         bool threadAbort;
-        bool gotKbdTrig;
-        int tick, nextEchoTick, jack_offset_tick, schedDelayTicks;
-        int lastKbdTick;
-        int lastLfoTick[20], nextLfoTick;
-        int lastSeqTick[20], nextSeqTick;
-        int nextNoteTick[20];
+        bool gotArpKbdTrig;
+        bool gotSeqKbdTrig;
+        int tick, jack_offset_tick, schedDelayTicks;
+        int lastSchedTick;
+        int nextLfoTick[20], nextMinLfoTick;
+        int nextSeqTick[20], nextMinSeqTick;
+        int nextArpTick[20], nextMinArpTick;
         int tempo, internal_tempo;
-        QVector<LfoSample> lfoData;
-        QVector<SeqSample> seqData;
+        QVector<Sample> lfoData;
+        Sample seqSample;
+
+        double tickToDelta(int tick);
+        int deltaToTick (double curtime);
+        double aTimeToDelta(snd_seq_real_time_t* atime);
+        const snd_seq_real_time_t* deltaToATime(double curtime);
+        void calcClockRatio();
 
-        void initSeqNotifier();
-        const snd_seq_real_time_t *tickToDelta(int tick);
-        int deltaToTick (snd_seq_real_time_t curtime);
-        void calcMidiRatio();
+        void schedEvent(MidiEvent outEv, int n_tick, int outport, int length = 0);
+        bool requestEchoAt(int echoTick, int infotag = 1);
+        bool handleEvent(MidiEvent inEv);
+        void handleEcho(MidiEvent inEv);
+        void resetTicks();
 
         JackSync *jackSync;
         jack_position_t jpos;
 
         int midiTick;
-        double m_ratio;
-        snd_seq_real_time_t delta, real_time, jack_offset;
+        double m_ratio;         /* duration of one tick, in nanoseconds; based on current tempo */
+        snd_seq_real_time_t delta, real_time;
         snd_seq_real_time_t tmptime;
-        
+
     public:
         bool forwardUnmatched, runQueueIfArp, runArp;
         int portUnmatched;
-        int grooveTick, grooveVelocity, grooveLength;
         bool use_midiclock, use_jacksync, trigByKbd;
 
     public:
-        SeqDriver(QList<MidiArp*> *p_midiArpList, 
+/*! @param p_midiArpList List of pointers to each MidiArp worker
+ *  @param p_midiLfoList List of pointers to each MidiLfo worker
+ *  @param p_midiSeqList List of pointers to each MidiSeq worker
+ *  @param parent QWidget ID of the parent Widget
+ */
+        SeqDriver(QList<MidiArp*> *p_midiArpList,
                 QList<MidiLfo *> *p_midiLfoList,
-                QList<MidiSeq *> *p_midiSeqList, QWidget* parent=0);
+                QList<MidiSeq *> *p_midiSeqList, int p_portCount, QWidget* parent=0);
         ~SeqDriver();
-        void registerPorts(int num);
-        int getPortCount();
-        void initArpQueue();
         void get_time();
-        void setQueueStatus(bool run);
         bool isModified();
         void setModified(bool);
         int getAlsaClientId();
         void run();
 
    signals:
-        void midiEvent(snd_seq_event_t *ev);
+        void midiEvent(int type, int data, int channel, int value);
         void controlEvent(int ccnumber, int channel, int value);
-        void noteEvent(int note, int velocity);
-        void jackShutdown(bool); //boolean is passed to main toolbar 
+        void jackShutdown(bool); //boolean is passed to main toolbar
                                 //jackSync button
 
    public slots:
         void setForwardUnmatched(bool on);
         void setPortUnmatched(int id);
+        void setQueueStatus(bool run);
         void setQueueTempo(int bpm);
-        void runQueue(bool);
-        void setGrooveTick(int);
-        void setGrooveVelocity(int);
-        void setGrooveLength(int);
-        void sendGroove();
         void setUseMidiClock(bool on);
         void setMidiControllable(bool on);
         void setUseJackTransport(bool on);
diff --git a/src/seqscreen.cpp b/src/seqscreen.cpp
index 9f3770a..be7ab2f 100644
--- a/src/seqscreen.cpp
+++ b/src/seqscreen.cpp
@@ -1,37 +1,35 @@
-/*
- *      seqscreen.cpp
- *      
- *      This file is part of QMidiArp.
- * 
+/*!
+ * @file seqscreen.cpp
+ * @brief Implementation of the SeqScreen class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *      MA 02110-1301, USA.
+ *
  */
 
-#include <QBrush>
 #include <QPainter>
 #include <QPaintDevice>
 #include <QPen>
 #include <QPixmap>
-#include <QPolygon>
-#include <QSize>
-#include <QSizePolicy>
-
-#include <alsa/asoundlib.h>
 
 #include "seqscreen.h"
-#include "arpdata.h"
+#include "engine.h"
 
 
 SeqScreen::SeqScreen(QWidget* parent) : QWidget (parent)
@@ -41,6 +39,7 @@ SeqScreen::SeqScreen(QWidget* parent) : QWidget (parent)
     mouseY = 0;
     recordMode = false;
     currentRecStep = false;
+    currentIndex = 0;
     isMuted = false;
 }
 
@@ -52,7 +51,6 @@ SeqScreen::~SeqScreen()
 void SeqScreen::paintEvent(QPaintEvent*)
 {
     QPainter p(this);
-    QPolygon points(7);
     QPen pen;
     pen.setWidth(1);
     p.setFont(QFont("Helvetica", 8));
@@ -63,23 +61,29 @@ void SeqScreen::paintEvent(QPaintEvent*)
     int beat = 4;
     int npoints = 0;
     int ypos, xpos, xscale, yscale;
-    int octYoffset;
     w = QWidget::width();
     h = QWidget::height();
     int notestreak_thick = 4;
     int ofs;
     int x, x1;
-    int octave = 0;
     int maxOctave = 4;
     int minOctave = 0;
     int beatRes = 1.0;
     int beatDiv = 0;
     int noctaves= 4;
     l2 = 0;
-    QChar c;
+
+    //Grid setup
+    if (p_data.isEmpty()) return;
+    nsteps = p_data.at(p_data.count() - 1).tick / TPQN;
+    beatRes = (p_data.count() - 1) / nsteps;
+    beatDiv = (beatRes * nsteps > 64) ? 64 / nsteps : beatRes;
+    npoints = beatRes * nsteps;
+    xscale = (w - 2 * SEQSCR_HMARG) / nsteps;
+    yscale = h - 2 * SEQSCR_VMARG;
 
     //Blue Filled Frame
-    if (isMuted) 
+    if (isMuted)
         p.fillRect(0, 0, w, h, QColor(70, 70, 70));
     else
         p.fillRect(0, 0, w, h, QColor(10, 10, 50));
@@ -87,47 +91,36 @@ void SeqScreen::paintEvent(QPaintEvent*)
     p.setWindow(0, 0, w, h);
     p.setPen(QColor(20, 20, 160));
     p.drawRect(0, 0, w - 1, h - 1);
-    
-
-    //Grid 
-    if (p_seqData.isEmpty()) return;
-    nsteps = p_seqData.at(p_seqData.count() - 1).tick / TICKS_PER_QUARTER;
-    beatRes = (p_seqData.count() - 1) / nsteps;
-    beatDiv = (beatRes * nsteps > 64) ? 64 / nsteps : beatRes;
-    npoints = beatRes * nsteps;
-    xscale = (w - 2 * SEQSCREEN_HMARGIN) / nsteps;
-    yscale = h - 2 * SEQSCREEN_VMARGIN;
 
     //Draw current record step
     if (recordMode)
-    p.fillRect(currentRecStep * xscale * nsteps / npoints + SEQSCREEN_HMARGIN
-                , SEQSCREEN_VMARGIN
+    p.fillRect(currentRecStep * xscale * nsteps / npoints + SEQSCR_HMARG
+                , SEQSCR_VMARG
                 , xscale * nsteps / npoints
-                , h - 2*SEQSCREEN_VMARGIN, QColor(5, 40, 100));
-
+                , h - 2*SEQSCR_VMARG, QColor(5, 40, 100));
 
     //Beat separators
     for (l1 = 0; l1 < nsteps + 1; l1++) {
 
         if (l1 < 10) {
-            ofs = w / nsteps * .5 - 4 + SEQSCREEN_HMARGIN;
+            ofs = w / nsteps * .5 - 4 + SEQSCR_HMARG;
         } else {
-            ofs = w / nsteps * .5 - 6 + SEQSCREEN_HMARGIN;
+            ofs = w / nsteps * .5 - 6 + SEQSCR_HMARG;
         }
         if ((bool)(l1%beat)) {
             p.setPen(QColor(60, 100, 180));
         } else {
-            p.setPen(QColor(100, 100, 180));      
+            p.setPen(QColor(100, 100, 180));
         }
         x = l1 * xscale;
-        p.drawLine(SEQSCREEN_HMARGIN + x, SEQSCREEN_VMARGIN,
-                SEQSCREEN_HMARGIN + x, h-SEQSCREEN_VMARGIN);
+        p.drawLine(SEQSCR_HMARG + x, SEQSCR_VMARG,
+                SEQSCR_HMARG + x, h-SEQSCR_VMARG);
 
         if (l1 < nsteps) {
-            p.setPen(QColor(100, 150, 180));      
+            p.setPen(QColor(100, 150, 180));
 
             //Beat numbers
-            p.drawText(ofs + x, SEQSCREEN_VMARGIN, QString::number(l1+1));
+            p.drawText(ofs + x, SEQSCR_VMARG, QString::number(l1+1));
 
             // Beat divisor separators
             p.setPen(QColor(20, 60, 120));
@@ -135,10 +128,10 @@ void SeqScreen::paintEvent(QPaintEvent*)
             for (l2 = 1; l2 < beatDiv; l2++) {
                 x1 = x + l2 * xscale / beatDiv;
                 if (x1 < xscale * nsteps)
-                    p.drawLine(SEQSCREEN_HMARGIN + x1,
-                            SEQSCREEN_VMARGIN, SEQSCREEN_HMARGIN + x1,
-                            h - SEQSCREEN_VMARGIN);
-            } 
+                    p.drawLine(SEQSCR_HMARG + x1,
+                            SEQSCR_VMARG, SEQSCR_HMARG + x1,
+                            h - SEQSCR_VMARG);
+            }
         }
     }
 
@@ -148,42 +141,35 @@ void SeqScreen::paintEvent(QPaintEvent*)
     for (l1 = 0; l1 <= noctaves * 12; l1++) {
         l3 = l1%12;
 
-        if (!l3) 
-            p.setPen(QColor(20, 60, 180));
-        else
-            p.setPen(QColor(10, 20, 100));
-            
-        ypos = yscale * l1 / noctaves / 12 + SEQSCREEN_VMARGIN;
-        p.drawLine(0, ypos, w - SEQSCREEN_HMARGIN, ypos);
+    if (!l3)
+        p.setPen(QColor(20, 60, 180));
+    else
+        p.setPen(QColor(10, 20, 100));
+
+        ypos = yscale * l1 / noctaves / 12 + SEQSCR_VMARG;
+        p.drawLine(0, ypos, w - SEQSCR_HMARG, ypos);
         if ((l3 == 2) || (l3 == 4) || (l3 == 6) || (l3 == 9) || (l3 == 11)) {
             pen.setColor(QColor(20, 60, 180));
             pen.setWidth(notestreak_thick);
             p.setPen(pen);
-            p.drawLine(0, ypos - notestreak_thick / 2, SEQSCREEN_HMARGIN / 2, 
+            p.drawLine(0, ypos - notestreak_thick / 2, SEQSCR_HMARG / 2,
             ypos- notestreak_thick / 2);
             pen.setWidth(1);
-            p.setPen(pen); 
-        }       
-/*        p.drawText(1, 
-                yscale * (l1) / noctaves + SEQSCREEN_VMARGIN + 4, 
-                QString::number((noctaves - l1) * 128 / noctaves));
-*/
-    }    
+            p.setPen(pen);
+        }
+    }
 
     //Draw function
 
-    octave = 0;
-
     pen.setWidth(notestreak_thick);
     p.setPen(pen);
     for (l1 = 0; l1 < npoints; l1++) {
 
-        octYoffset = 0;
         x = l1 * xscale * nsteps / npoints;
-        ypos = yscale - yscale * (p_seqData.at(l1).value - 36) / noctaves / 12
-                        + SEQSCREEN_VMARGIN - notestreak_thick / 2;
-        xpos = SEQSCREEN_HMARGIN + x + notestreak_thick / 2;
-        if (p_seqData.at(l1).muted) {  
+        ypos = yscale - yscale * (p_data.at(l1).value - 36) / noctaves / 12
+                        + SEQSCR_VMARG - pen.width() / 2;
+        xpos = SEQSCR_HMARG + x + pen.width() / 2;
+        if (p_data.at(l1).muted) {
             pen.setColor(QColor(5, 40, 100));
             p.setPen(pen);
         }
@@ -192,70 +178,74 @@ void SeqScreen::paintEvent(QPaintEvent*)
             p.setPen(pen);
         }
         p.drawLine(xpos, ypos,
-                        xpos + (xscale / beatRes) - notestreak_thick / 2, ypos);
+                        xpos + (xscale / beatRes) - pen.width(), ypos);
     }
-    ypos = int((mouseY - SEQSCREEN_VMARGIN + 3)/4) * 4 + SEQSCREEN_VMARGIN - 2;
+    ypos = int((mouseY - SEQSCR_VMARG + 3)/4) * 4 + SEQSCR_VMARG - 2;
     pen.setWidth(2);
     pen.setColor(QColor(50, 160, 220));
     p.setPen(pen);
-    p.drawLine(SEQSCREEN_HMARGIN / 2, ypos,
-                        SEQSCREEN_HMARGIN *2 / 3, ypos);
-}
+    p.drawLine(SEQSCR_HMARG / 2, ypos,
+                        SEQSCR_HMARG *2 / 3, ypos);
 
+    // Cursor
+    pen.setWidth(notestreak_thick * 2);
+    pen.setColor(QColor(50, 180, 220));
+    p.setPen(pen);
+    x = currentIndex * xscale * (int)nsteps / npoints;
+    xpos = SEQSCR_HMARG + x + pen.width() / 2;
+    p.drawLine(xpos, h - 2,
+                    xpos + (xscale / beatRes) - pen.width(), h - 2);
 
-void SeqScreen::updateScreen(const QVector<SeqSample>& seqData)
-{
-    p_seqData = seqData;
-    update();
 }
 
-void SeqScreen::setMuted(bool on)
+void SeqScreen::updateScreen(const QVector<Sample>& data)
 {
-    isMuted = on;
+    p_data = data;
     update();
 }
 
-QSize SeqScreen::sizeHint() const
+void SeqScreen::updateScreen(int p_index)
 {
-    return QSize(SEQSCREEN_MINIMUM_WIDTH, SEQSCREEN_MINIMUM_HEIGHT); 
+    currentIndex = p_index;
+    update();
 }
 
-QSizePolicy SeqScreen::sizePolicy() const
+void SeqScreen::setMuted(bool on)
 {
-    return QSizePolicy(QSizePolicy::MinimumExpanding,
-            QSizePolicy::MinimumExpanding);
+    isMuted = on;
+    update();
 }
 
 void SeqScreen::mouseMoveEvent(QMouseEvent *event)
 {
     mouseX = event->x();
     mouseY = event->y();
-    if ((mouseX < SEQSCREEN_HMARGIN)|| (mouseX >= w - SEQSCREEN_HMARGIN))
+    if ((mouseX < SEQSCR_HMARG)|| (mouseX >= w - SEQSCR_HMARG))
         return;
-    if ((mouseY <= SEQSCREEN_VMARGIN)|| (mouseY > h - SEQSCREEN_VMARGIN))
-        return;        
-    emit seqMouseMoved(((double)mouseX - SEQSCREEN_HMARGIN) / 
-                            (w - 2 * SEQSCREEN_HMARGIN), 
-                1. - ((double)mouseY - SEQSCREEN_VMARGIN) / 
-                (h - 2 * SEQSCREEN_VMARGIN), event->buttons());
-    
+    if ((mouseY <= SEQSCR_VMARG)|| (mouseY > h - SEQSCR_VMARG))
+        return;
+    emit mouseMoved(((double)mouseX - SEQSCR_HMARG) /
+                            (w - 2 * SEQSCR_HMARG),
+                1. - ((double)mouseY - SEQSCR_VMARG) /
+                (h - 2 * SEQSCR_VMARG), event->buttons());
+
 }
 
 void SeqScreen::mousePressEvent(QMouseEvent *event)
 {
     mouseX = event->x();
     mouseY = event->y();
-    if ((mouseX < SEQSCREEN_HMARGIN)|| (mouseX >= w - SEQSCREEN_HMARGIN))
+    if ((mouseX < SEQSCR_HMARG)|| (mouseX >= w - SEQSCR_HMARG))
         return;
-    if ((mouseY <= SEQSCREEN_VMARGIN)|| (mouseY > h - SEQSCREEN_VMARGIN))
+    if ((mouseY <= SEQSCR_VMARG)|| (mouseY > h - SEQSCR_VMARG))
         return;
-    emit seqMousePressed(((double)mouseX - SEQSCREEN_HMARGIN) / 
-                            (w - 2 * SEQSCREEN_HMARGIN), 
-                1. - ((double)mouseY - SEQSCREEN_VMARGIN) / 
-                (h - 2 * SEQSCREEN_VMARGIN), event->buttons());
+    emit mousePressed(((double)mouseX - SEQSCR_HMARG) /
+                            (w - 2 * SEQSCR_HMARG),
+                1. - ((double)mouseY - SEQSCR_VMARG) /
+                (h - 2 * SEQSCR_VMARG), event->buttons());
 }
 
-void SeqScreen::setRecord(bool on)
+void SeqScreen::setRecordMode(bool on)
 {
     recordMode = on;
 }
@@ -264,3 +254,14 @@ void SeqScreen::setCurrentRecStep(int recStep)
 {
     currentRecStep = recStep;
 }
+
+QSize SeqScreen::sizeHint() const
+{
+    return QSize(SEQSCR_MIN_W, SEQSCR_MIN_H);
+}
+
+QSizePolicy SeqScreen::sizePolicy() const
+{
+    return QSizePolicy(QSizePolicy::MinimumExpanding,
+            QSizePolicy::MinimumExpanding);
+}
diff --git a/src/seqscreen.h b/src/seqscreen.h
index bbcc9fe..8bc0060 100644
--- a/src/seqscreen.h
+++ b/src/seqscreen.h
@@ -1,54 +1,93 @@
+/*!
+ * @file seqscreen.h
+ * @brief Header for the SeqScreen class
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef SEQSCREEN_H
 #define SEQSCREEN_H
 
 #include <QLabel>
 #include <QMouseEvent>
-#include <QSize>
-#include <QSizePolicy>
 #include <QString>
 #include <QTimer>
 #include <QWidget>
-#include "midiseq.h"
+#include <QSizePolicy>
+#include <QSize>
 
-#define SEQSCREEN_MINIMUM_WIDTH   180
-#define SEQSCREEN_MINIMUM_HEIGHT  212
-#define SEQSCREEN_VMARGIN          10
-#define SEQSCREEN_HMARGIN          20
+#include "midiseq.h"
 
+#define SEQSCR_MIN_W   180
+#define SEQSCR_MIN_H   212
+#define SEQSCR_VMARG    10
+#define SEQSCR_HMARG    20
 
+/*! @brief Drawing widget for visualization of sequences using QPainter
+ *
+ * SeqScreen is created and embedded by SeqWidget. The painter callback
+ * produces a streak map of a waveform as a piano roll display. The
+ * display is updated by calling SeqScreen::updateScreen() with the
+ * Sample vector as argument. A cursor is placed at the corresponding
+ * vector index by calling SeqScreen::updateScreen() with the integer
+ * current index as an overloaded member.
+ * SeqScreen emits mouse events corresponding to the Qt mousePressed()
+ * and mouseMoved() events. The mouse position is transferred as a
+ * double from 0 ... 1.0 representing the relative mouse position on the
+ * entire SeqScreen display area.
+ */
 class SeqScreen : public QWidget
 {
   Q_OBJECT
 
   private:
     //QTimer *timer;
-    QVector<SeqSample> p_seqData, seqData;
+    QVector<Sample> p_data, data;
     int mouseX, mouseY;
     int w, h;
     bool recordMode;
     int currentRecStep;
+    int currentIndex;
     bool isMuted;
 
   protected:
     virtual void paintEvent(QPaintEvent *);
-   
+
   public:
     SeqScreen(QWidget* parent=0);
     ~SeqScreen();
     virtual QSize sizeHint() const;
     virtual QSizePolicy sizePolicy() const;
-    
+
   signals:
-    void seqMousePressed(double, double, int);
-    void seqMouseMoved(double, double, int);
-    
-  public slots: 
-    void updateScreen(const QVector<SeqSample>& seqData);
+    void mousePressed(double, double, int);
+    void mouseMoved(double, double, int);
+
+  public slots:
+    void updateScreen(const QVector<Sample>& data);
+    void updateScreen(int currentIndex);
     void mouseMoveEvent(QMouseEvent* event);
     void mousePressEvent(QMouseEvent* event);
-    void setRecord(bool on);
+    void setRecordMode(bool on);
     void setCurrentRecStep(int currentRecStep);
     void setMuted(bool on);
 };
-  
+
 #endif
diff --git a/src/seqwidget.cpp b/src/seqwidget.cpp
index af5c05c..40b5c7f 100644
--- a/src/seqwidget.cpp
+++ b/src/seqwidget.cpp
@@ -1,18 +1,21 @@
-/*
- *      seqwidget.cpp
- *      
+/*!
+ * @file seqwidget.cpp
+ * @brief Implements the SeqWidget GUI class.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
@@ -41,6 +44,25 @@
 SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QWidget *parent):
     QWidget(parent), midiWorker(p_midiWorker), modified(false)
 {
+    int l1;
+
+    // QSignalMappers allow identifying signal senders for MIDI learn/forget
+    learnSignalMapper = new QSignalMapper(this);
+    connect(learnSignalMapper, SIGNAL(mapped(int)),
+             this, SLOT(midiLearn(int)));
+
+    forgetSignalMapper = new QSignalMapper(this);
+    connect(forgetSignalMapper, SIGNAL(mapped(int)),
+             this, SLOT(midiForget(int)));
+
+    // we need the cancel MIDI Learn action only once for all
+    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
+    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
+    cancelMidiLearnAction->setEnabled(false);
+
+    midiCCNames << "MuteToggle" << "Velocity" << "NoteLength"
+                << "RecordToggle" << "unknown";
+
     // Management Buttons on the right top
     QHBoxLayout *manageBoxLayout = new QHBoxLayout;
 
@@ -49,13 +71,13 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
     QToolButton *renameButton = new QToolButton(this);
     renameButton->setDefaultAction(renameAction);
     connect(renameAction, SIGNAL(triggered()), this, SLOT(moduleRename()));
-    
+
     deleteAction = new QAction(QIcon(arpremove_xpm), tr("&Delete..."), this);
     deleteAction->setToolTip(tr("Delete this Sequencer"));
     QToolButton *deleteButton = new QToolButton(this);
     deleteButton->setDefaultAction(deleteAction);
     connect(deleteAction, SIGNAL(triggered()), this, SLOT(moduleDelete()));
-    
+
     manageBoxLayout->addStretch();
     manageBoxLayout->addWidget(renameButton);
     manageBoxLayout->addWidget(deleteButton);
@@ -68,37 +90,72 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
     connect(enableNoteIn, SIGNAL(toggled(bool)), this, SLOT(updateEnableNoteIn(bool)));
     enableNoteInLabel->setBuddy(enableNoteIn);
     enableNoteIn->setToolTip(tr("Transpose the sequence following incoming notes"));
-    
+
+    QLabel *enableNoteOffLabel = new QLabel(tr("&Note Off"),inBox);
+    enableNoteOff = new QCheckBox(this);
+    connect(enableNoteOff, SIGNAL(toggled(bool)), this, SLOT(updateEnableNoteOff(bool)));
+    enableNoteOffLabel->setBuddy(enableNoteOff);
+    enableNoteOff->setToolTip(tr("Stop output when Note is released"));
+
     QLabel *enableVelInLabel = new QLabel(tr("&Velocity"),inBox);
     enableVelIn = new QCheckBox(this);
     connect(enableVelIn, SIGNAL(toggled(bool)), this, SLOT(updateEnableVelIn(bool)));
     enableVelInLabel->setBuddy(enableVelIn);
     enableVelIn->setToolTip(tr("Set sequence velocity to that of incoming notes"));
-    
+
+    QLabel *enableRestartByKbdLabel = new QLabel(tr("&Restart"),inBox);
+    enableRestartByKbd = new QCheckBox(this);
+    connect(enableRestartByKbd, SIGNAL(toggled(bool)), this, SLOT(updateEnableRestartByKbd(bool)));
+    enableRestartByKbdLabel->setBuddy(enableRestartByKbd);
+    enableRestartByKbd->setToolTip(tr("Restart sequence when a new note is received"));
+
+    QLabel *enableTrigByKbdLabel = new QLabel(tr("&Trigger"),inBox);
+    enableTrigByKbd = new QCheckBox(this);
+    connect(enableTrigByKbd, SIGNAL(toggled(bool)), this, SLOT(updateEnableTrigByKbd(bool)));
+    enableTrigByKbdLabel->setBuddy(enableTrigByKbd);
+    enableTrigByKbd->setToolTip(tr("Retrigger sequence when a new note is received"));
+
+    QLabel *enableLoopLabel = new QLabel(tr("&Loop"),inBox);
+    enableLoop = new QCheckBox(this);
+    connect(enableLoop, SIGNAL(toggled(bool)), this, SLOT(updateEnableLoop(bool)));
+    enableLoopLabel->setBuddy(enableLoop);
+    enableLoop->setToolTip(tr("Play sequence as loop instead of a single run"));
+
     enableNoteIn->setChecked(true);
+    enableNoteOff->setChecked(false);
     enableVelIn->setChecked(true);
-    
+    enableRestartByKbd->setChecked(false);
+    enableTrigByKbd->setChecked(false);
+    enableLoop->setChecked(true);
+
     QLabel *chInLabel = new QLabel(tr("&Channel"), inBox);
-    chIn = new QSpinBox(inBox);
-    chIn->setRange(1, 16);
-    chIn->setKeyboardTracking(false);
+    chIn = new QComboBox(inBox);
+    for (l1 = 0; l1 < 16; l1++) chIn->addItem(QString::number(l1 + 1));
     chInLabel->setBuddy(chIn);
-    connect(chIn, SIGNAL(valueChanged(int)), this, SLOT(updateChIn(int)));
+    connect(chIn, SIGNAL(activated(int)), this, SLOT(updateChIn(int)));
 
     QGridLayout *inBoxLayout = new QGridLayout;
 
     inBoxLayout->addWidget(enableNoteInLabel, 0, 0);
     inBoxLayout->addWidget(enableNoteIn, 0, 1);
-    inBoxLayout->addWidget(enableVelInLabel, 1, 0);
-    inBoxLayout->addWidget(enableVelIn, 1, 1);
-    inBoxLayout->addWidget(chInLabel, 2, 0);
-    inBoxLayout->addWidget(chIn, 2, 1);
+    inBoxLayout->addWidget(enableNoteOffLabel, 1, 0);
+    inBoxLayout->addWidget(enableNoteOff, 1, 1);
+    inBoxLayout->addWidget(enableVelInLabel, 2, 0);
+    inBoxLayout->addWidget(enableVelIn, 2, 1);
+    inBoxLayout->addWidget(enableRestartByKbdLabel, 3, 0);
+    inBoxLayout->addWidget(enableRestartByKbd, 3, 1);
+    inBoxLayout->addWidget(enableTrigByKbdLabel, 4, 0);
+    inBoxLayout->addWidget(enableTrigByKbd, 4, 1);
+    inBoxLayout->addWidget(enableLoopLabel, 5, 0);
+    inBoxLayout->addWidget(enableLoop, 5, 1);
+    inBoxLayout->addWidget(chInLabel, 6, 0);
+    inBoxLayout->addWidget(chIn, 6, 1);
     if (compactStyle) {
         inBoxLayout->setSpacing(1);
         inBoxLayout->setMargin(2);
     }
 
-    inBox->setLayout(inBoxLayout); 
+    inBox->setLayout(inBoxLayout);
 
 
     // Output group box on right bottom
@@ -106,40 +163,37 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
 
     QLabel *muteLabel = new QLabel(tr("&Mute"),portBox);
     muteOut = new QCheckBox(this);
-
-    cancelMidiLearnAction = new QAction(tr("Cancel MIDI &Learning"), this);
-    connect(cancelMidiLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnCancel()));
-    cancelMidiLearnAction->setEnabled(false);
-
     muteOut->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+    connect(muteOut, SIGNAL(toggled(bool)), this, SLOT(setMuted(bool)));
+    muteLabel->setBuddy(muteOut);
+
     QAction *muteLearnAction = new QAction(tr("MIDI &Learn"), this);
     muteOut->addAction(muteLearnAction);
-    connect(muteLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnMute()));
+    connect(muteLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(muteLearnAction, 0);
+
     QAction *muteForgetAction = new QAction(tr("MIDI &Forget"), this);
     muteOut->addAction(muteForgetAction);
-    connect(muteForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetMute()));
+    connect(muteForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(muteForgetAction, 0);
 
     muteOut->addAction(cancelMidiLearnAction);
 
-    connect(muteOut, SIGNAL(toggled(bool)), this, SLOT(setMuted(bool)));
-    muteLabel->setBuddy(muteOut);
 
     QLabel *portLabel = new QLabel(tr("&Port"), portBox);
-    portOut = new QSpinBox(portBox);
+    portOut = new QComboBox(portBox);
     portLabel->setBuddy(portOut);
-    portOut->setRange(1, portCount);
-    portOut->setKeyboardTracking(false);
-    connect(portOut, SIGNAL(valueChanged(int)), this, SLOT(updatePortOut(int)));
+    for (l1 = 0; l1 < portCount; l1++) portOut->addItem(QString::number(l1 + 1));
+    connect(portOut, SIGNAL(activated(int)), this, SLOT(updatePortOut(int)));
 
     QLabel *channelLabel = new QLabel(tr("C&hannel"), portBox);
-    channelOut = new QSpinBox(portBox);
+    channelOut = new QComboBox(portBox);
     channelLabel->setBuddy(channelOut);
-    channelOut->setRange(1, 16);
-    channelOut->setKeyboardTracking(false);
-    connect(channelOut, SIGNAL(valueChanged(int)), this,
+    for (l1 = 0; l1 < 16; l1++) channelOut->addItem(QString::number(l1 + 1));
+    connect(channelOut, SIGNAL(activated(int)), this,
             SLOT(updateChannelOut(int)));
 
+
     QGridLayout *portBoxLayout = new QGridLayout;
     portBoxLayout->addWidget(muteLabel, 0, 0);
     portBoxLayout->addWidget(muteOut, 0, 1);
@@ -162,19 +216,19 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
     inOutBoxLayout->addWidget(inBox);
     inOutBoxLayout->addWidget(portBox);
     inOutBoxLayout->addStretch();
-    
+
     // group box for sequence setup
     QGroupBox *seqBox = new QGroupBox(tr("Sequence"), this);
 
-    screen = new SeqScreen(this); 
+    screen = new SeqScreen(this);
     screen->setToolTip(
         tr("Right button to mute points, left button to draw custom wave"));
-    screen->setMinimumHeight(SEQSCREEN_MINIMUM_HEIGHT);
-    connect(screen, SIGNAL(seqMouseMoved(double, double, int)), this,
+    screen->setMinimumHeight(SEQSCR_MIN_H);
+    connect(screen, SIGNAL(mouseMoved(double, double, int)), this,
             SLOT(mouseMoved(double, double, int)));
-    connect(screen, SIGNAL(seqMousePressed(double, double, int)), this,
+    connect(screen, SIGNAL(mousePressed(double, double, int)), this,
             SLOT(mousePressed(double, double, int)));
-            
+
     QLabel *waveFormBoxLabel = new QLabel(tr("&Sequence"), seqBox);
     waveFormBox = new QComboBox(seqBox);
     waveFormBoxLabel->setBuddy(waveFormBox);
@@ -185,15 +239,28 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
     waveFormBox->setMinimumContentsLength(8);
     connect(waveFormBox, SIGNAL(activated(int)), this,
             SLOT(updateWaveForm(int)));
-    
+
     QLabel *recordButtonLabel = new QLabel(tr("Re&cord"), seqBox);
-    QAction *recordAction = new QAction(QIcon(seqrecord_xpm), tr("Re&cord"), seqBox);
+    recordAction = new QAction(QIcon(seqrecord_xpm), tr("Re&cord"), seqBox);
     recordAction->setToolTip(tr("Record step by step"));
     recordAction->setCheckable(true);
     QToolButton *recordButton = new QToolButton(seqBox);
     recordButton->setDefaultAction(recordAction);
     recordButtonLabel->setBuddy(recordButton);
     connect(recordAction, SIGNAL(toggled(bool)), this, SLOT(setRecord(bool)));
+    recordButton->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
+
+    QAction *recordLearnAction = new QAction(tr("MIDI &Learn"), this);
+    recordButton->addAction(recordLearnAction);
+    connect(recordLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(recordLearnAction, 3);
+
+    QAction *recordForgetAction = new QAction(tr("MIDI &Forget"), this);
+    recordButton->addAction(recordForgetAction);
+    connect(recordForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(recordForgetAction, 3);
+
+    recordButton->addAction(cancelMidiLearnAction);
 
     QLabel *resBoxLabel = new QLabel(tr("&Resolution"),
             seqBox);
@@ -221,15 +288,15 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
     sizeBox->setMinimumContentsLength(3);
     connect(sizeBox, SIGNAL(activated(int)), this,
             SLOT(updateSize(int)));
-    
-    copyToCustomButton = new QToolButton(this); 
+
+    copyToCustomButton = new QToolButton(this);
     copyToCustomAction = new QAction( QIcon(seqwavcp_xpm),
             tr("C&opy to new wave"), this);
     connect(copyToCustomAction, SIGNAL(triggered()), this,
             SLOT(copyToCustom()));
     copyToCustomButton->setDefaultAction(copyToCustomAction);
-    
-//temporarily hide these elements until multiple patterns are implemented  
+
+    //temporarily hide these elements until multiple patterns are implemented
     copyToCustomAction->setEnabled(false);
     waveFormBox->setEnabled(false);
     copyToCustomButton->setVisible(false);
@@ -239,44 +306,48 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
 
     velocity = new Slider(0, 127, 1, 8, 64, Qt::Horizontal,
             tr("Veloc&ity"), seqBox);
-            
     velocity->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
+    connect(velocity, SIGNAL(sliderMoved(int)), this,
+            SLOT(updateVelocity(int)));
+
+
     QAction *velocityLearnAction = new QAction(tr("MIDI &Learn"), this);
     velocity->addAction(velocityLearnAction);
-    connect(velocityLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnVel()));
+    connect(velocityLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(velocityLearnAction, 1);
+
     QAction *velocityForgetAction = new QAction(tr("MIDI &Forget"), this);
     velocity->addAction(velocityForgetAction);
-    connect(velocityForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetVel()));
-    
+    connect(velocityForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(velocityForgetAction, 1);
+
     velocity->addAction(cancelMidiLearnAction);
 
-    connect(velocity, SIGNAL(valueChanged(int)), this,
-            SLOT(updateVelocity(int)));
-            
+
     notelength = new Slider(0, 127, 1, 16, 64, Qt::Horizontal,
             tr("N&ote Length"), seqBox);
-
     notelength->setContextMenuPolicy(Qt::ContextMenuPolicy(Qt::ActionsContextMenu));
-    
-    QAction *notelengthLearnAction = new QAction(tr("MIDI &Learn"), this);
-    notelength->addAction(notelengthLearnAction);
-    connect(notelengthLearnAction, SIGNAL(triggered()), this, SLOT(midiLearnNoteLen()));
-    QAction *notelengthForgetAction = new QAction(tr("MIDI &Forget"), this);
-    notelength->addAction(notelengthForgetAction);
-    connect(notelengthForgetAction, SIGNAL(triggered()), this, SLOT(midiForgetNoteLen()));
-    
-    notelength->addAction(cancelMidiLearnAction);
-    
     connect(notelength, SIGNAL(valueChanged(int)), this,
             SLOT(updateNoteLength(int)));
-            
+
+    QAction *noteLengthLearnAction = new QAction(tr("MIDI &Learn"), this);
+    notelength->addAction(noteLengthLearnAction);
+    connect(noteLengthLearnAction, SIGNAL(triggered()), learnSignalMapper, SLOT(map()));
+    learnSignalMapper->setMapping(noteLengthLearnAction, 2);
+
+    QAction *noteLengthForgetAction = new QAction(tr("MIDI &Forget"), this);
+    notelength->addAction(noteLengthForgetAction);
+    connect(noteLengthForgetAction, SIGNAL(triggered()), forgetSignalMapper, SLOT(map()));
+    forgetSignalMapper->setMapping(noteLengthForgetAction, 2);
+
+    notelength->addAction(cancelMidiLearnAction);
+
     transpose = new Slider(-24, 24, 1, 2, 0, Qt::Horizontal,
             tr("&Transpose"), seqBox);
-    connect(transpose, SIGNAL(valueChanged(int)), this,
+    connect(transpose, SIGNAL(sliderMoved(int)), this,
             SLOT(updateTranspose(int)));
 
-    
+
     QGridLayout* sliderLayout = new QGridLayout;
     sliderLayout->addWidget(copyToCustomButton, 0 , 0);
     sliderLayout->addWidget(velocity, 1, 0);
@@ -298,17 +369,17 @@ SeqWidget::SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QW
     paramBoxLayout->addWidget(sizeBoxLabel, 3, 0);
     paramBoxLayout->addWidget(sizeBox, 3, 1);
     paramBoxLayout->setRowStretch(4, 1);
-    
+
     QGridLayout* seqBoxLayout = new QGridLayout;
     seqBoxLayout->addWidget(screen, 0, 0, 1, 2);
     seqBoxLayout->addLayout(paramBoxLayout, 1, 0);
     seqBoxLayout->addLayout(sliderLayout, 1, 1);
     if (compactStyle) {
-	    seqBoxLayout->setMargin(2);
-	    seqBoxLayout->setSpacing(1);
-	}
-    seqBox->setLayout(seqBoxLayout); 
-    
+        seqBoxLayout->setMargin(2);
+        seqBoxLayout->setSpacing(1);
+    }
+    seqBox->setLayout(seqBoxLayout);
+
     QHBoxLayout *widgetLayout = new QHBoxLayout;
     widgetLayout->addWidget(seqBox, 1);
     widgetLayout->addLayout(inOutBoxLayout, 0);
@@ -334,25 +405,33 @@ void SeqWidget::writeData(QXmlStreamWriter& xml)
 {
     QByteArray tempArray;
     int l1;
-    
+
     xml.writeStartElement(name.left(3));
     xml.writeAttribute("name", name.mid(name.indexOf(':') + 1));
         xml.writeStartElement("input");
             xml.writeTextElement("enableNote", QString::number(
                 midiWorker->enableNoteIn));
+            xml.writeTextElement("enableNote", QString::number(
+                midiWorker->enableNoteOff));
             xml.writeTextElement("enableVelocity", QString::number(
                 midiWorker->enableVelIn));
+            xml.writeTextElement("restartByKbd", QString::number(
+                midiWorker->restartByKbd));
+            xml.writeTextElement("trigByKbd", QString::number(
+                midiWorker->trigByKbd));
+            xml.writeTextElement("enableLoop", QString::number(
+                midiWorker->enableLoop));
             xml.writeTextElement("channel", QString::number(
                 midiWorker->chIn));
         xml.writeEndElement();
-        
+
         xml.writeStartElement("output");
             xml.writeTextElement("port", QString::number(
                 midiWorker->portOut));
             xml.writeTextElement("channel", QString::number(
                 midiWorker->channelOut));
         xml.writeEndElement();
-    
+
         xml.writeStartElement("seqParams");
             xml.writeTextElement("resolution", QString::number(
                 resBox->currentIndex()));
@@ -365,7 +444,7 @@ void SeqWidget::writeData(QXmlStreamWriter& xml)
             xml.writeTextElement("transp", QString::number(
                 midiWorker->transp));
         xml.writeEndElement();
-      
+
         tempArray.clear();
         l1 = 0;
         while (l1 < midiWorker->muteMask.count()) {
@@ -375,7 +454,7 @@ void SeqWidget::writeData(QXmlStreamWriter& xml)
         xml.writeStartElement("muteMask");
             xml.writeTextElement("data", tempArray.toHex());
         xml.writeEndElement();
-        
+
         tempArray.clear();
         l1 = 0;
         while (l1 < midiWorker->muteMask.count()) {
@@ -385,7 +464,7 @@ void SeqWidget::writeData(QXmlStreamWriter& xml)
         xml.writeStartElement("sequence");
             xml.writeTextElement("data", tempArray.toHex());
         xml.writeEndElement();
-           
+
         xml.writeStartElement("midiControllers");
         for (int l1 = 0; l1 < ccList.count(); l1++) {
             xml.writeStartElement("MIDICC");
@@ -397,64 +476,22 @@ void SeqWidget::writeData(QXmlStreamWriter& xml)
             xml.writeEndElement();
         }
         xml.writeEndElement();
-        
+
     xml.writeEndElement();
 }
 
-void SeqWidget::writeDataText(QTextStream& arpText)
-{
-    int l1 = 0;
-    arpText << midiWorker->enableNoteIn << ' ' 
-        << midiWorker->enableVelIn << ' '
-        << midiWorker->chIn << '\n';
-    arpText << midiWorker->channelOut << ' ' 
-        << midiWorker->portOut << ' '
-        << midiWorker->notelength << '\n';
-    arpText << resBox->currentIndex() << ' '
-        << sizeBox->currentIndex() << ' '
-        << midiWorker->vel << ' '
-        << midiWorker->transp << '\n';
-    arpText << "MIDICC" << endl;
-    for (int l1 = 0; l1 < ccList.count(); l1++) {
-        arpText << ccList.at(l1).ID << ' '
-                << ccList.at(l1).ccnumber << ' '
-                << ccList.at(l1).channel << ' '
-                << ccList.at(l1).min << ' '
-                << ccList.at(l1).max << endl;
-    }
-    arpText << "EOCC" << endl;
-
-    arpText << waveFormBox->currentIndex() << '\n';
-    // Write Mute Mask
-    while (l1 < midiWorker->muteMask.count()) {
-        arpText << midiWorker->muteMask.at(l1) << ' ';
-        l1++;
-        if (!(l1 % 32)) arpText << "\n";
-    }
-    arpText << "EOM\n"; // End Of Mute
-    // Write Custom Sequence
-    l1 = 0;
-    while (l1 < midiWorker->customWave.count()) {
-        arpText << midiWorker->customWave.at(l1).value << ' ';
-        l1++;
-        if (!(l1 % 16)) arpText << "\n";
-    }
-    arpText << "EOS\n"; // End Of Wave
-    modified = false;
-}                                      
-
 void SeqWidget::readData(QXmlStreamReader& xml)
 {
-    int ctrlID, ccnumber, channel, min, max;
+    int controlID, ccnumber, channel, min, max;
     int tmp;
     int wvtmp = 0;
-    SeqSample seqSample;
-    
+    Sample sample;
+
     while (!xml.atEnd()) {
         xml.readNext();
         if (xml.isEndElement())
             break;
-            
+
         else if (xml.isStartElement() && (xml.name() == "input")) {
             while (!xml.atEnd()) {
                 xml.readNext();
@@ -462,10 +499,21 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                     break;
                 if (xml.name() == "enableNote")
                     enableNoteIn->setChecked(xml.readElementText().toInt());
+                else if (xml.name() == "enableNoteOff")
+                    enableNoteOff->setChecked(xml.readElementText().toInt());
                 else if (xml.name() == "enableVelocity")
                     enableVelIn->setChecked(xml.readElementText().toInt());
-                else if (xml.name() == "channel")
-                    chIn->setValue(xml.readElementText().toInt() + 1);
+                else if (xml.name() == "restartByKbd")
+                    enableRestartByKbd->setChecked(xml.readElementText().toInt());
+                else if (xml.name() == "trigByKbd")
+                    enableTrigByKbd->setChecked(xml.readElementText().toInt());
+                else if (xml.name() == "enableLoop")
+                    enableLoop->setChecked(xml.readElementText().toInt());
+                else if (xml.name() == "channel") {
+                    tmp = xml.readElementText().toInt();
+                    chIn->setCurrentIndex(tmp);
+                    updateChIn(tmp);
+                }
                 else skipXmlElement(xml);
             }
         }
@@ -475,10 +523,16 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                 xml.readNext();
                 if (xml.isEndElement())
                     break;
-                if (xml.name() == "channel")
-                    channelOut->setValue(xml.readElementText().toInt() + 1);
-                else if (xml.name() == "port")
-                    portOut->setValue(xml.readElementText().toInt() + 1);
+                if (xml.name() == "channel") {
+                    tmp = xml.readElementText().toInt();
+                    channelOut->setCurrentIndex(tmp);
+                    updateChannelOut(tmp);
+                }
+                else if (xml.name() == "port") {
+                    tmp = xml.readElementText().toInt();
+                    portOut->setCurrentIndex(tmp);
+                    updatePortOut(tmp);
+                }
                 else skipXmlElement(xml);
             }
         }
@@ -498,12 +552,19 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                     sizeBox->setCurrentIndex(tmp);
                     updateSize(tmp);
                 }
-                else if (xml.name() == "velocity")
-                    velocity->setValue(xml.readElementText().toInt());
-                else if (xml.name() == "noteLength")
+                else if (xml.name() == "velocity") {
+                    tmp = xml.readElementText().toInt();
+                    velocity->setValue(tmp);
+                    updateVelocity(tmp);
+                }
+                else if (xml.name() == "noteLength") {
                     notelength->setValue(xml.readElementText().toInt() / 2);
-                else if (xml.name() == "transp")
-                    transpose->setValue(xml.readElementText().toInt());
+                }
+                else if (xml.name() == "transp") {
+                    tmp = xml.readElementText().toInt();
+                    transpose->setValue(tmp);
+                    updateTranspose(tmp);
+                }
                 else skipXmlElement(xml);
             }
         }
@@ -514,7 +575,7 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                     break;
                 if (xml.isStartElement() && (xml.name() == "data")) {
                     midiWorker->muteMask.clear();
-                    QByteArray tmpArray = 
+                    QByteArray tmpArray =
                             QByteArray::fromHex(xml.readElementText().toLatin1());
                     for (int l1 = 0; l1 < tmpArray.count(); l1++) {
                         midiWorker->muteMask.append(tmpArray.at(l1));
@@ -530,15 +591,15 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                     break;
                 if (xml.isStartElement() && (xml.name() == "data")) {
                     midiWorker->customWave.clear();
-                    QByteArray tmpArray = 
+                    QByteArray tmpArray =
                             QByteArray::fromHex(xml.readElementText().toLatin1());
-                    int step = TICKS_PER_QUARTER / midiWorker->res;
+                    int step = TPQN / midiWorker->res;
                     int lt = 0;
                     for (int l1 = 0; l1 < tmpArray.count(); l1++) {
-                        seqSample.value = tmpArray.at(l1);
-                        seqSample.tick = lt;
-                        seqSample.muted = midiWorker->muteMask.at(l1);
-                        midiWorker->customWave.append(seqSample);
+                        sample.value = tmpArray.at(l1);
+                        sample.tick = lt;
+                        sample.muted = midiWorker->muteMask.at(l1);
+                        midiWorker->customWave.append(sample);
                         lt+=step;
                     }
                 }
@@ -551,7 +612,7 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                 if (xml.isEndElement())
                     break;
                 if (xml.isStartElement() && (xml.name() == "MIDICC")) {
-                    ctrlID = xml.attributes().value("CtrlID").toString().toInt();
+                    controlID = xml.attributes().value("CtrlID").toString().toInt();
                     ccnumber = -1;
                     channel = -1;
                     min = -1;
@@ -570,10 +631,10 @@ void SeqWidget::readData(QXmlStreamReader& xml)
                             max = xml.readElementText().toInt();
                         else skipXmlElement(xml);
                     }
-                    
+
                     if ((-1 < ccnumber) && (-1 < channel) && (-1 < min) && (-1 < max))
-                        appendMidiCC(ctrlID, ccnumber, channel, min, max);
-                    else qWarning("Controller data incomplete");                  
+                        appendMidiCC(controlID, ccnumber, channel, min, max);
+                    else qWarning("Controller data incomplete");
                 }
                 else skipXmlElement(xml);
             }
@@ -591,47 +652,47 @@ void SeqWidget::skipXmlElement(QXmlStreamReader& xml)
         qWarning("Unknown Element in XML File: %s",qPrintable(xml.name().toString()));
         while (!xml.atEnd()) {
             xml.readNext();
-    
+
             if (xml.isEndElement())
                 break;
-    
+
             if (xml.isStartElement()) {
                 skipXmlElement(xml);
             }
         }
     }
 }
- 
+
 void SeqWidget::readDataText(QTextStream& arpText)
 {
     QString qs, qs2;
     int l1, lt, wvtmp;
-    SeqSample seqSample;
-    
+    Sample sample;
+
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
+    qs2 = qs.section(' ', 0, 0);
     enableNoteIn->setChecked(qs2.toInt());
-    qs2 = qs.section(' ', 1, 1); 
+    qs2 = qs.section(' ', 1, 1);
     enableVelIn->setChecked(qs2.toInt());
-    qs2 = qs.section(' ', 2, 2); 
-    chIn->setValue(qs2.toInt() + 1);
+    qs2 = qs.section(' ', 2, 2);
+    chIn->setCurrentIndex(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
-    channelOut->setValue(qs2.toInt() + 1);
-    qs2 = qs.section(' ', 1, 1); 
-    portOut->setValue(qs2.toInt() + 1);
-    qs2 = qs.section(' ', 2, 2); 
+    qs2 = qs.section(' ', 0, 0);
+    channelOut->setCurrentIndex(qs2.toInt());
+    qs2 = qs.section(' ', 1, 1);
+    portOut->setCurrentIndex(qs2.toInt());
+    qs2 = qs.section(' ', 2, 2);
     notelength->setValue(qs2.toInt());
     qs = arpText.readLine();
-    qs2 = qs.section(' ', 0, 0); 
+    qs2 = qs.section(' ', 0, 0);
     resBox->setCurrentIndex(qs2.toInt());
     updateRes(qs2.toInt());
-    qs2 = qs.section(' ', 1, 1); 
+    qs2 = qs.section(' ', 1, 1);
     sizeBox->setCurrentIndex(qs2.toInt());
     updateSize(qs2.toInt());
-    qs2 = qs.section(' ', 2, 2); 
+    qs2 = qs.section(' ', 2, 2);
     velocity->setValue(qs2.toInt());
-    qs2 = qs.section(' ', 3, 3); 
+    qs2 = qs.section(' ', 3, 3);
     transpose->setValue(qs2.toInt());
     qs = arpText.readLine();
     if (qs == "MIDICC")
@@ -639,7 +700,7 @@ void SeqWidget::readDataText(QTextStream& arpText)
         qs = arpText.readLine();
         while (qs != "EOCC") {
             qs2 = qs.section(' ', 0, 0);
-            int ctrlID = qs2.toInt();
+            int controlID = qs2.toInt();
             qs2 = qs.section(' ', 1, 1);
             int ccnumber = qs2.toInt();
             qs2 = qs.section(' ', 2, 2);
@@ -648,16 +709,16 @@ void SeqWidget::readDataText(QTextStream& arpText)
             int min = qs2.toInt();
             qs2 = qs.section(' ', 4, 4);
             int max = qs2.toInt();
-            appendMidiCC(ctrlID, ccnumber, channel, min, max);
+            appendMidiCC(controlID, ccnumber, channel, min, max);
             qs = arpText.readLine();
         }
     qs = arpText.readLine();
     }
 
     wvtmp = qs.toInt();
-    
+
     // Read Mute Mask
-    int step = TICKS_PER_QUARTER / midiWorker->res;
+    int step = TPQN / midiWorker->res;
     qs = arpText.readLine();
     if (qs.isEmpty() || (qs == "EOP")) return;
     qs2 = qs.section(' ', 0, 0);
@@ -669,7 +730,7 @@ void SeqWidget::readDataText(QTextStream& arpText)
         if (!(l1%32)) qs = arpText.readLine();
         qs2 = qs.section(' ', l1%32, l1%32);
     }
-    
+
     // Read Custom Waveform
     qs = arpText.readLine();
     qs2 = qs.section(' ', 0, 0);
@@ -677,10 +738,10 @@ void SeqWidget::readDataText(QTextStream& arpText)
     l1 = 0;
     lt = 0;
     while (qs2 !="EOS") {
-        seqSample.value=qs2.toInt();
-        seqSample.tick = lt;
-        seqSample.muted = midiWorker->muteMask.at(l1);
-        midiWorker->customWave.append(seqSample);
+        sample.value=qs2.toInt();
+        sample.tick = lt;
+        sample.muted = midiWorker->muteMask.at(l1);
+        midiWorker->customWave.append(sample);
         lt+=step;
         l1++;
         if (!(l1%16)) qs = arpText.readLine();
@@ -689,7 +750,7 @@ void SeqWidget::readDataText(QTextStream& arpText)
     waveFormBox->setCurrentIndex(wvtmp);
     updateWaveForm(wvtmp);
     modified = false;
-}                                      
+}
 
 void SeqWidget::loadWaveForms()
 {
@@ -710,55 +771,49 @@ void SeqWidget::setEnableVelIn(bool on)
 
 void SeqWidget::setChIn(int value)
 {
-    chIn->setValue(value);
+    chIn->setCurrentIndex(value);
     modified = true;
 }
 
-void SeqWidget::setMuted(bool on)
-{
-    midiWorker->setMuted(on);
-    screen->setMuted(on);
-}
-
-void SeqWidget::setPortOut(int value)
+void SeqWidget::updateChIn(int value)
 {
-    portOut->setValue(value);
+    midiWorker->chIn = value;
     modified = true;
 }
 
-void SeqWidget::setChannelOut(int value)
+void SeqWidget::updateEnableNoteIn(bool on)
 {
-    channelOut->setValue(value);
+    midiWorker->enableNoteIn = on;
     modified = true;
 }
 
-void SeqWidget::updateChIn(int value)
+void SeqWidget::updateEnableNoteOff(bool on)
 {
-    midiWorker->chIn = value - 1;
+    midiWorker->enableNoteOff = on;
     modified = true;
 }
 
-void SeqWidget::updateEnableNoteIn(bool on)
+void SeqWidget::updateEnableVelIn(bool on)
 {
-    midiWorker->enableNoteIn = on;
+    midiWorker->enableVelIn = on;
     modified = true;
 }
 
-void SeqWidget::updateEnableVelIn(bool on)
+void SeqWidget::updateEnableRestartByKbd(bool on)
 {
-    midiWorker->enableVelIn = on;
+    midiWorker->restartByKbd = on;
     modified = true;
 }
 
-void SeqWidget::updatePortOut(int value)
+void SeqWidget::updateEnableTrigByKbd(bool on)
 {
-    midiWorker->portOut = value - 1;
+    midiWorker->trigByKbd = on;
     modified = true;
 }
 
-void SeqWidget::updateChannelOut(int value)
+void SeqWidget::updateEnableLoop(bool on)
 {
-    midiWorker->channelOut = value - 1;
+    midiWorker->enableLoop = on;
     modified = true;
 }
 
@@ -771,26 +826,27 @@ void SeqWidget::updateNoteLength(int val)
 void SeqWidget::updateWaveForm(int val)
 {
     midiWorker->updateWaveForm(val);
-    midiWorker->getData(&seqData);
-    screen->updateScreen(seqData);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
 void SeqWidget::setRecord(bool on)
 {
     recordMode = on;
-    screen->setRecord(on);
+    midiWorker->setRecordMode(on);
+    screen->setRecordMode(on);
     screen->setCurrentRecStep(midiWorker->currentRecStep);
-    screen->updateScreen(seqData);
+    screen->updateScreen(data);
 }
 
 void SeqWidget::updateRes(int val)
 {
     midiWorker->res = seqResValues[val];
     midiWorker->resizeAll();
-    midiWorker->getData(&seqData);
+    midiWorker->getData(&data);
     screen->setCurrentRecStep(midiWorker->currentRecStep);
-    screen->updateScreen(seqData);
+    screen->updateScreen(data);
     modified = true;
 }
 
@@ -798,9 +854,9 @@ void SeqWidget::updateSize(int val)
 {
     midiWorker->size = val + 1;
     midiWorker->resizeAll();
-    midiWorker->getData(&seqData);
+    midiWorker->getData(&data);
     screen->setCurrentRecStep(midiWorker->currentRecStep);
-    screen->updateScreen(seqData);
+    screen->updateScreen(data);
     modified = true;
 }
 
@@ -823,10 +879,9 @@ void SeqWidget::processNote(int note, int vel)
         if (enableVelIn->isChecked()) velocity->setValue(vel);
     }
     else {
-        midiWorker->recordNote(note);
-        midiWorker->getData(&seqData);
+        midiWorker->getData(&data);
         screen->setCurrentRecStep(midiWorker->currentRecStep);
-        screen->updateScreen(seqData);
+        screen->updateScreen(data);
     }
 }
 
@@ -842,13 +897,13 @@ void SeqWidget::mouseMoved(double mouseX, double mouseY, int buttons)
 {
     if (buttons == 2) {
         midiWorker->setMutePoint(mouseX, lastMute);
-    } 
+    }
     else {
         midiWorker->setCustomWavePoint(mouseX, mouseY);
         screen->setCurrentRecStep(midiWorker->currentRecStep);
     }
-    midiWorker->getData(&seqData);
-    screen->updateScreen(seqData);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
     modified = true;
 }
 
@@ -860,8 +915,38 @@ void SeqWidget::mousePressed(double mouseX, double mouseY, int buttons)
         midiWorker->setCustomWavePoint(mouseX, mouseY);
         screen->setCurrentRecStep(midiWorker->currentRecStep);
     }
-    midiWorker->getData(&seqData);
-    screen->updateScreen(seqData);
+    midiWorker->getData(&data);
+    screen->updateScreen(data);
+    modified = true;
+}
+
+void SeqWidget::setMuted(bool on)
+{
+    midiWorker->setMuted(on);
+    screen->setMuted(on);
+}
+
+void SeqWidget::setPortOut(int value)
+{
+    portOut->setCurrentIndex(value);
+    modified = true;
+}
+
+void SeqWidget::setChannelOut(int value)
+{
+    channelOut->setCurrentIndex(value);
+    modified = true;
+}
+
+void SeqWidget::updatePortOut(int value)
+{
+    midiWorker->portOut = value;
+    modified = true;
+}
+
+void SeqWidget::updateChannelOut(int value)
+{
+    midiWorker->channelOut = value;
     modified = true;
 }
 
@@ -886,49 +971,41 @@ void SeqWidget::moduleDelete()
             == QMessageBox::No) {
         return;
     }
-    emit seqRemove(ID);
+    emit moduleRemove(ID);
 }
 
 void SeqWidget::moduleRename()
 {
     QString newname, oldname;
     bool ok;
-    
+
     oldname = name;
 
     newname = QInputDialog::getText(this, APP_NAME,
                 tr("New Name"), QLineEdit::Normal, oldname.mid(4), &ok);
-                
+
     if (ok && !newname.isEmpty()) {
         name = "Seq:" + newname;
         emit dockRename(name, parentDockID);
     }
 }
 
-void SeqWidget::appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int max)
+void SeqWidget::appendMidiCC(int controlID, int ccnumber, int channel, int min, int max)
 {
     MidiCC midiCC;
     int l1 = 0;
-    switch (ctrlID) {
-        case 0: midiCC.name = "MuteToggle";
-        break;
-        case 1: midiCC.name = "Velocity";
-        break;
-        case 2: midiCC.name = "NoteLength";
-        break;
-        default: midiCC.name = "Unknown";
-    }
-    midiCC.ID = ctrlID;
+    midiCC.name = midiCCNames.at(controlID);
+    midiCC.ID = controlID;
     midiCC.ccnumber = ccnumber;
     midiCC.channel = channel;
     midiCC.min = min;
     midiCC.max = max;
-    
-    while ( (l1 < ccList.count()) && 
-        ((ctrlID != ccList.at(l1).ID) || 
+
+    while ( (l1 < ccList.count()) &&
+        ((controlID != ccList.at(l1).ID) ||
         (ccnumber != ccList.at(l1).ccnumber) ||
         (channel != ccList.at(l1).channel)) ) l1++;
-    
+
     if (ccList.count() == l1) {
         ccList.append(midiCC);
         qWarning("MIDI Controller %d appended for %s"
@@ -938,15 +1015,15 @@ void SeqWidget::appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int
         qWarning("MIDI Controller %d already attributed to %s"
                 , ccnumber, qPrintable(midiCC.name));
     }
-        
+
     cancelMidiLearnAction->setEnabled(false);
     modified = true;
 }
 
-void SeqWidget::removeMidiCC(int ctrlID, int ccnumber, int channel)
+void SeqWidget::removeMidiCC(int controlID, int ccnumber, int channel)
 {
     for (int l1 = 0; l1 < ccList.count(); l1++) {
-        if (ccList.at(l1).ID == ctrlID) {
+        if (ccList.at(l1).ID == controlID) {
             if (((ccList.at(l1).ccnumber == ccnumber)
                     && (ccList.at(l1).channel == channel))
                     || (0 > channel)) {
@@ -959,40 +1036,16 @@ void SeqWidget::removeMidiCC(int ctrlID, int ccnumber, int channel)
     modified = true;
 }
 
-void SeqWidget::midiLearnMute()
-{
-    emit setMidiLearn(parentDockID, ID, 0);
-    qWarning("Requesting Midi Learn for MuteToggle");
-    cancelMidiLearnAction->setEnabled(true);
-}
-
-void SeqWidget::midiForgetMute()
-{
-    removeMidiCC(0, 0, -1);
-}
-
-void SeqWidget::midiLearnNoteLen()
-{
-    emit setMidiLearn(parentDockID, ID, 2);
-    qWarning("Requesting Midi Learn for NoteLength");
-    cancelMidiLearnAction->setEnabled(true);
-}
-
-void SeqWidget::midiForgetNoteLen()
-{
-    removeMidiCC(2, 0, -1);
-}
-
-void SeqWidget::midiLearnVel()
+void SeqWidget::midiLearn(int controlID)
 {
-    emit setMidiLearn(parentDockID, ID, 1);
-    qWarning("Requesting Midi Learn for Velocity");
+    emit setMidiLearn(parentDockID, ID, controlID);
+    qWarning("Requesting Midi Learn for %s", qPrintable(midiCCNames.at(controlID)));
     cancelMidiLearnAction->setEnabled(true);
 }
 
-void SeqWidget::midiForgetVel()
+void SeqWidget::midiForget(int controlID)
 {
-    removeMidiCC(1, 0, -1);
+    removeMidiCC(controlID, 0, -1);
 }
 
 void SeqWidget::midiLearnCancel()
diff --git a/src/seqwidget.h b/src/seqwidget.h
index a0ab155..7658050 100644
--- a/src/seqwidget.h
+++ b/src/seqwidget.h
@@ -1,18 +1,21 @@
-/*
- *      seqwidget.h
- *      
+/*!
+ * @file seqwidget.h
+ * @brief Member definitions for the SeqWidget GUI class.
+ *
+ * @section LICENSE
+ *
  *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
- *      
+ *
  *      This program is free software; you can redistribute it and/or modify
  *      it under the terms of the GNU General Public License as published by
  *      the Free Software Foundation; either version 2 of the License, or
  *      (at your option) any later version.
- *      
+ *
  *      This program is distributed in the hope that it will be useful,
  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *      GNU General Public License for more details.
- *      
+ *
  *      You should have received a copy of the GNU General Public License
  *      along with this program; if not, write to the Free Software
  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
@@ -24,6 +27,7 @@
 
 #include <QString>
 #include <QComboBox>
+#include <QSignalMapper>
 #include <QSpinBox>
 #include <QTextStream>
 #include <QCheckBox>
@@ -36,101 +40,406 @@
 #include "slider.h"
 #include "seqscreen.h"
 
+/*! @brief This array holds the currently available resolution values.
+ */
 const int seqResValues[5] = {1, 2, 4, 8, 16};
 #ifndef MIDICC_H
+
+/*! @brief Structure holding all elements of a MIDI controller allocated to
+ * a QMidiArp parameter assigned via MIDI learn
+ */
 struct MidiCC {
-        QString name;
-        int min;
-        int max;
-        int ccnumber;
-        int channel;
-        int ID;
-    };    
+        QString name;   /**< @brief Name of the assigned parameter (GUI element)*/
+        int min;        /**< @brief Value output when the CC value is 0 */
+        int max;        /**< @brief Value output when the CC value is 127 */
+        int ccnumber;   /**< @brief MIDI CC number of the assigned controller event */
+        int channel;    /**< @brief MIDI channel on which the controller has to come in */
+        int ID;         /**< @brief Internal ID of the assigned parameter (GUI element)*/
+    };
 #define MIDICC_H
 #endif
 
+/*!
+ * @brief GUI class associated with and controlling a MidiSeq worker
+
+ * It controls the MidiSeq sequencer and
+ * is created alongwith each MidiSeq and embedded in a DockWidget on
+ * MainWindow level. It can read its parameter set from an XML stream
+ * by calling its readData member. It manages a SeqWidget::ccList
+ * for each
+ * instance for MIDI controllers attributed through the MIDILearn
+ * context menu. It instantiates an SeqScreen and interacts with it.
+ *
+*/
 class SeqWidget : public QWidget
 {
     Q_OBJECT
 
-    QSpinBox *chIn;
-    QSpinBox *channelOut, *portOut;
+    QComboBox *chIn;
+    QComboBox *channelOut, *portOut;
     QComboBox *waveFormBox, *resBox, *sizeBox, *freqBox;
     QAction *copyToCustomAction;
     QAction *deleteAction, *renameAction;
     QAction *cancelMidiLearnAction;
+    QSignalMapper *learnSignalMapper, *forgetSignalMapper;
     QToolButton *copyToCustomButton;
- 
+    QStringList midiCCNames; /**< List of GUI-element names with index = ControlID */
+
     MidiSeq *midiWorker;
-    QVector<SeqSample> seqData;
-    bool modified, lastMute;
-    bool recordMode;
+    QVector<Sample> data;
+    bool modified;      /**< Is set to True if unsaved parameter modifications exist */
+    bool lastMute;      /**< Contains the mute state of the last waveForm point modified by mouse click*/
+/*!
+* @brief This function allows ignoring one XML element in the XML stream
+* passed by the caller.
+*
+* It also advances the stream read-in. It is used to
+* ignore unknown elements for both-ways-compatibility
+*
+* @param xml reference to QXmlStreamReader containing the open XML stream
+*/
+    void skipXmlElement(QXmlStreamReader& xml);
+/*!
+* @brief This function populates the SeqWidget::waveForms list with
+* waveform names, currently only one.
+*
+* This might be used in the future for handling sequence presets
+*
+* @par Currently there is only one waveform
+*   - 0 Custom
+*
+*/
+    void loadWaveForms();
 
+    bool recordMode;    /**< Is set to True if incoming notes are to be step-recorded*/
+
+/* PUBLIC MEMBERS */
   public:
-    QString name;
+/*!
+ * @brief Constructor for SeqWidget. It creates the GUI and an SeqScreen
+ * instance.
+ *
+ * @param p_midiWorker Associated MidiSeq Object
+ * @param portCount Number of available ALSA MIDI output ports
+ * @param compactStyle If set to True, Widget will use reduced spacing and small fonts
+ * @param parent The parent widget of this module, i.e. MainWindow
+ */
+    SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QWidget* parent=0);
+    ~SeqWidget();
+
+    QString name;       /**< @brief The name of this SeqWidget as shown in the DockWidget TitleBar */
+    int ID;                     /**< @brief Corresponds to the Engine::midiSeqList index of the associated MidiSeq */
+    int parentDockID;           /**< @brief The index of the ArpWidget's parent DockWidget in Engine::moduleWindowList */
+    QVector<MidiCC> ccList;     /**< @brief Contains MIDI controller - GUI element bindings */
+
     SeqScreen *screen;
     QStringList waveForms;
     QCheckBox *muteOut;
-    QCheckBox *enableNoteIn;               
-    QCheckBox *enableVelIn; 
+    QCheckBox *enableNoteIn;
+    QCheckBox *enableNoteOff;
+    QCheckBox *enableVelIn;
+    QCheckBox *enableRestartByKbd;
+    QCheckBox *enableTrigByKbd;
+    QCheckBox *enableLoop;
     Slider *velocity, *transpose, *notelength;
-    int ID, parentDockID;
-    QVector<MidiCC> ccList;
-        
-    SeqWidget(MidiSeq *p_midiWorker, int portCount, bool compactStyle, QWidget* parent=0);
-    ~SeqWidget();
+    QAction *recordAction;
+
+    void setChIn(int value);
+    void setEnableNoteIn(bool on);
+    void setEnableVelIn(bool on);
+
+/*!
+* @brief This function returns the MidiSeq instance associated with this GUI
+* Widget.
+* @return MidiSeq instance associated with this GUI
+*/
     MidiSeq *getMidiWorker();
-    
+
+/*!
+* @brief This function reads all parameters of this module from an XML stream
+* passed by the caller, i.e. MainWindow.
+*
+* @param xml QXmlStreamWriter to read from
+*/
     void readData(QXmlStreamReader& xml);
-    void skipXmlElement(QXmlStreamReader& xml);
+/*!
+* @brief This function reads all module parameters of this module from an old
+* QMidiArp .qma text stream.
+*
+* @param arpText QTextStream to read from
+*/
     void readDataText(QTextStream& arpText);
+/*!
+* @brief This function writes all parameters of this module to an XML stream
+* passed by the caller, i.e. MainWindow.
+*
+* @param xml QXmlStreamWriter to write to
+*/
     void writeData(QXmlStreamWriter& xml);
-    void writeDataText(QTextStream& arpText);
-    void setChIn(int value);
-    void setEnableNoteIn(bool on);
-    void setEnableVelIn(bool on);
+/*!
+* @brief This function is obsolete.
+* It writes to an old QMidiArp .qma text file passed as a stream
+* by MainWindow.
+*
+* @param arpText QTextStream to write to
+*/
     void setChannelOut(int value);
+/*!
+* @brief Settor for the SeqWidget::portOut spinbox setting the output
+* port of this module.
+* @param value Number of the output port to send data to
+*
+*/
     void setPortOut(int value);
-    void loadWaveForms();
+/*!
+* @brief Accessor for SeqWidget::modified.
+* @return True if unsaved parameter modifications exist
+*
+*/
     bool isModified();
+/*!
+* @brief This function sets SeqWidget::modified.
+* @param m Set to True if unsaved parameter modifications appear
+*
+*/
     void setModified(bool);
-  
+
+/* SIGNALS */
   signals:
+/*! @brief Currently not in use. */
     void patternChanged();
-    void seqRemove(int ID);
-    void dockRename(const QString& name, int parentDockID);  
+/*! @brief Emitted to MainWindow::removeSeq for module deletion.
+ *  @param ID The internal SeqWidget::ID of the module to remove
+ *  */
+    void moduleRemove(int ID);
+/*! @brief Emitted to MainWindow::renameDock for module rename.
+ *  @param mname New name of the module
+ *  @param parentDockID SeqWidget::parentDockID of the module to rename
+ * */
+    void dockRename(const QString& mname, int parentDockID);
+/*! @brief Emitted to Engine::setMidiLearn to listen for incoming events.
+ *  @param parentDockID SeqWidget::parentDockID of the module to rename
+ *  @param ID SeqWidget::ID of the module receiving the MIDI controller
+ *  @param controlID ID of the GUI element to be assigned to the controller
+ *  */
     void setMidiLearn(int parentDockID, int ID, int controlID);
-    void setMidiForget(int parentDockID, int ID);
-    
+/*! @brief Forwarded context menu action by signalMapper to call MIDI-Learn/Forget functions.
+ *  @param controlID ID of the GUI element requesting the MIDI controller
+ *  */
+    void triggered(int controlID);
+
+/* PUBLIC SLOTS */
   public slots:
-    void updateChIn(int value);
-    void updateEnableNoteIn(bool on);
-    void updateEnableVelIn(bool on);
-    void updateChannelOut(int value);
-    void setMuted(bool on);
-    void updatePortOut(int value);
+/*!
+* @brief Slot currently not in use.
+* @param val Waveform index to choose as present in SeqWidget::loadWaveForms.
+*
+*/
     void updateWaveForm(int);
-    void setRecord(bool on);
-    void processNote(int note, int velocity);
+/*!
+* @brief Slot for the SeqWidget::resBox combobox. Sets the resolution
+* of the sequencer.
+*
+* It sets MidiSeq::res and updates the SeqScreen of this module.
+* @param val Resolution index from SeqWidget::seqResValues to set.
+*
+*/
     void updateRes(int);
+/*!
+* @brief Slot for the SeqWidget::sizeBox combobox. Sets the waveform size
+* of the sequencer.
+*
+* It sets MidiSeq::size and updates the SeqScreen of this module.
+* @param val Size (number of bars) of the waveform.
+*
+*/
     void updateSize(int);
-    void updateNoteLength(int val);
+/*!
+* @brief Slot for the SeqWidget::velocity slider. Sets the note velocity
+* of the sequencer.
+*
+* @param val New note velocity (0 ... 127).
+*
+*/
     void updateVelocity(int val);
+/*!
+* @brief Slot for the SeqWidget::transpose slider. Sets the global transpose
+* of the sequencer in semitones (-24 ... 24).
+*
+* @param val New global transpose of the sequencer in semitones (-24 ... 24).
+*
+*/
+    void updateNoteLength(int val);
     void updateTranspose(int val);
+
+    void updateChIn(int value);
+    void updateEnableNoteIn(bool on);
+    void updateEnableVelIn(bool on);
+    void updateEnableNoteOff(bool on);
+    void updateEnableRestartByKbd(bool on);
+    void updateEnableTrigByKbd(bool on);
+    void updateEnableLoop(bool on);
+    void setRecord(bool on);
+/*!
+* @brief Slot currently not in use.
+*
+* This function calls the midi worker
+* MidiSeq::copyToCustom, which copies the
+* current sequence to the custom sequence buffer
+*
+* It switches the waveForm combobox to index 0 and calls
+* SeqWidget::updateWaveForm.
+*
+*/
+    void copyToCustom();
+/*!
+* @brief Receiver for incoming note events. This function either sets the
+* global transpose and velocity or records the received note.
+*
+* Depending on the state of the SeqWidget::enableNoteIn and
+* SeqWidget::enableVelIn checkboxes, global transpose and velocity
+* are set according to the received note. If recordMode is set,
+* MidiSeq::recordNote is called, which will replace the current sequence
+* sample by the new note. Then, the screen is updated with the
+* new sequence (waveform).
+*
+* @param note Note value (0 ... 127) of the received note event.
+* @param velocity Velocity value (0 ... 127) of the received note event.
+*
+*/
+    void processNote(int note, int velocity);
+
+/*!
+* @brief Slot for the SeqScreen::mouseMoved signal. This function
+* mutes or sets a wave point when the mouse is moved with held buttons.
+*
+* The mouse events are generated by the SeqScreen.
+* It sets a MidiSeq::setCustomWavePoint and calls SeqWidget::newCustomOffset
+* if the left button is held while moving. It sets a MidiSeq::setMutePoint
+* if the right button is held while moving.
+*
+* @param mouseX Normalized mouse position on SeqScreen in X
+* direction (0.0 ... 1.0)
+* @param mouseY Normalized mouse position on SeqScreen in Y
+* direction (0.0 ... 1.0)
+* @param buttons 1 for left mouse button, 2 for right mouse button
+*
+*/
     void mouseMoved(double, double, int);
+/*!
+* @brief Slot for the SeqScreen::mousePressed signal. This function mutes or
+* sets a wave point when a mouse button is pressed before moving.
+*
+* The mouse events are generated by the SeqScreen.
+* It sets a MidiSeq::setCustomWavePoint and calls SeqWidget::newCustomOffset
+* if the left button is pressed. It toggles a MidiSeq::setMutePoint
+* if the right button is pressed before moving.
+*
+* @param mouseX Normalized mouse position on SeqScreen in X
+* direction (0.0 ... 1.0)
+* @param mouseY Normalized mouse position on SeqScreen in Y
+* direction (0.0 ... 1.0)
+* @param buttons 1 for left mouse button, 2 for right mouse button
+*
+*/
     void mousePressed(double, double, int);
-    void copyToCustom();
+
+/*!
+* @brief Slot for the SeqWidget::channelOut spinbox setting the output
+* channel of this module.
+* @param value Number of the output channel to send data to
+*
+*/
+    void updateChannelOut(int value);
+/*!
+* @brief Slot for the SeqWidget::portOut spinbox setting the output
+* port of this module.
+* @param value Number of the output port to send data to
+*
+*/
+    void updatePortOut(int value);
+/*!
+* @brief Slot for the SeqWidget::muteOut checkbox.
+* This function suppresses output of Seq data.
+*
+* It callsS
+* MidiSeq::setMuted and SeqScreen::setMuted
+* @param on Set to True for muting this module
+*
+*/
+    void setMuted(bool on);
+/*!
+* @brief Slot for SeqWidget::deleteAction.
+*
+* This function displays a warning and then emits the
+* SeqWidget::moduleRemove signal to MainWindow with the module ID as
+* parameter.
+*/
     void moduleDelete();
+/*!
+* @brief Slot for SeqWidget::renameAction.
+*
+* This function queries a new name then emits the SeqWidget::dockRename
+* signal to MainWindow with the new name and the dockWidget ID to rename.
+*/
     void moduleRename();
-    void appendMidiCC(int ctrlID, int ccnumber, int channel, int min, int max);
-    void removeMidiCC(int ctrlID, int ccnumber, int channel);
-    void midiLearnMute();
-    void midiForgetMute();
-    void midiLearnVel();
-    void midiForgetVel();
-    void midiLearnNoteLen();
-    void midiForgetNoteLen();
+/*!
+* @brief This function appends a new MIDI controller - GUI element
+* binding to SeqWidget::ccList.
+*
+* Before appending, it checks whether this binding already exists.
+* @param controlID The ID of the control GUI element (found
+* in SeqWidget::midiCCNames)
+* @param ccnumber The CC of the MIDI controller to be attributed
+* @param channel The MIDI Channel of the MIDI controller to be attributed
+* @param min The minimum value to which the controller range is mapped
+* @param max The maximum value to which the controller range is mapped
+*/
+    void appendMidiCC(int controlID, int ccnumber, int channel, int min, int max);
+/*!
+* @brief This function removes a MIDI controller - GUI element
+* binding from the SeqWidget::ccList.
+*
+* @param controlID The ID of the control GUI element (found
+* in SeqWidget::midiCCNames)
+* @param ccnumber The CC of the MIDI controller to be removed
+* @param channel The MIDI Channel of the MIDI controller to be removed
+*/
+    void removeMidiCC(int controlID, int ccnumber, int channel);
+/*!
+* @brief Slot for SeqWidget::triggered signal created by MIDI-Learn context
+* menu MIDI Learn action.
+*
+* This function sets Engine into
+* MIDI Learn status for this module and controlID.
+* It emits SeqWidget::setMidiLearn with the necessary module and GUI element
+* information parameters.
+* Engine will then wait for an incoming controller event and trigger the
+* attribution by calling appendMidiCC.
+*
+* @param controlID The ID of the control GUI element (found
+* in SeqWidget::midiCCNames)
+*/
+    void midiLearn(int controlID);
+/*!
+* @brief Slot for SeqWidget::triggered signal created by a
+* MIDI-Learn context menu Forget action.
+*
+* This function removes a controller
+* binding attribution by calling removeMidiCC.
+*
+* @param controlID The ID of the control GUI element (found
+* in SeqWidget::midiCCNames)
+*/
+    void midiForget(int controlID);
+/*!
+* @brief Slot for SeqWidget::cancelMidiLearnAction in MIDI-Learn
+* context menu. This function signals cancellation of the
+* MIDI-Learn Process to Engine.
+*
+* It emits SeqWidget::setMidiLearn with controlID set to -1 meaning cancel.
+*/
     void midiLearnCancel();
 };
-  
+
 #endif
diff --git a/src/slider.cpp b/src/slider.cpp
index 4ddca0f..0cbcdb0 100644
--- a/src/slider.cpp
+++ b/src/slider.cpp
@@ -1,3 +1,31 @@
+/*!
+ * @file slider.cpp
+ * @brief Widget class combining slider and spinbox and label.
+ *
+ * The spinbox and slider are coupled so that the slider value is
+ * visualized. The stepwidth when controller by keyboard cursor keys
+ * can be set as well as the display orientation.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #include <QBoxLayout>
 #include <QLabel>
 
@@ -21,17 +49,19 @@ Slider::Slider(int minValue, int maxValue, int pageStep, int tickStep,
         slider->setMinimumWidth(150);
     }
     connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateSpinBox(int)));
+    connect(slider, SIGNAL(sliderMoved(int)), this, SLOT(fillSpinBox(int)));
 
     sliderSpin = new QSpinBox(this);
     sliderSpin->setRange(minValue, maxValue);
     sliderSpin->setValue(value);
     sliderSpin->setKeyboardTracking(false);
     connect(sliderSpin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
+    connect(sliderSpin, SIGNAL(editingFinished()), this, SLOT(emitAsMoved()));
 
     QLabel* sliderLabel = new QLabel(this);
     sliderLabel->setText(label);
     sliderLabel->setBuddy(sliderSpin);
-    sliderLabel->setMinimumWidth(4*sliderLabel->fontMetrics().maxWidth());
+    sliderLabel->setMinimumWidth(5*sliderLabel->fontMetrics().maxWidth());
     QBoxLayout *sliderLayout = new QBoxLayout(QBoxLayout::LeftToRight,this);
     sliderLayout->setMargin(0);
     sliderLayout->addWidget(sliderLabel);
@@ -46,6 +76,7 @@ Slider::Slider(int minValue, int maxValue, int pageStep, int tickStep,
         sliderLayout->setDirection(QBoxLayout::LeftToRight);
         sliderLayout->setAlignment(Qt::AlignTop);
     }
+    setMinimumWidth(155 + sliderLabel->width() + sliderLabel->fontMetrics().maxWidth() * 3);
     setLayout(sliderLayout);
 }
 
@@ -69,3 +100,14 @@ void Slider::updateSpinBox(int val)
     sliderSpin->setValue(val);
 }
 
+void Slider::fillSpinBox(int val)
+{
+    emit(sliderMoved(val));
+    sliderSpin->setValue(val);
+}
+
+void Slider::emitAsMoved()
+{
+    emit(sliderMoved(sliderSpin->value()));
+}
+
diff --git a/src/slider.h b/src/slider.h
index 52722b8..bc95259 100644
--- a/src/slider.h
+++ b/src/slider.h
@@ -1,35 +1,74 @@
+/*!
+ * @file slider.h
+ * @brief Widget class combining slider and spinbox and label.
+ *
+ * The spinbox and slider are coupled so that the slider value is
+ * visualized. The stepwidth when controller by keyboard cursor keys
+ * can be set as well as the display orientation.
+ *
+ * @section LICENSE
+ *
+ *      Copyright 2009, 2010, 2011 <qmidiarp-devel at lists.sourceforge.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ *
+ */
 #ifndef SLIDER_H
 #define SLIDER_H
 
 #include <QString>
 #include <QSlider>
-#include <QSpinBox> 
+#include <QSpinBox>
 #include <QWidget>
 
 
 class Slider : public QWidget
 
+/*!
+ * @brief Widget class combining slider and spinbox and label.
+ *
+ * The spinbox and slider are coupled so that the slider value is
+ * visualized. The stepwidth when controller by keyboard cursor keys
+ * can be set as well as the display orientation.
+ *
+ */
 {
   Q_OBJECT
 
   private:
     QSlider *slider;
     QSpinBox *sliderSpin;
-    
+
   public:
-    Slider(int minValue, int maxValue, int pageStep, int tickStep, int value, 
+    Slider(int minValue, int maxValue, int pageStep, int tickStep, int value,
            Qt::Orientation orientation, const QString& label, QWidget * parent);
     ~Slider();
     int value();
-    
+
   signals:
     void valueChanged(int);
-    
+    void sliderMoved(int);
+
   public slots:
     void setValue(int);
-    
+
   private slots:
-    void updateSpinBox(int);  
+    void updateSpinBox(int);
+    void fillSpinBox(int);
+    void emitAsMoved();
 };
-  
+
 #endif
diff --git a/src/translations/qmidiarp_cs.ts b/src/translations/qmidiarp_cs.ts
new file mode 100644
index 0000000..037f7c4
--- /dev/null
+++ b/src/translations/qmidiarp_cs.ts
@@ -0,0 +1,1355 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="cs_CZ">
+<context>
+    <name>ArpWidget</name>
+    <message>
+        <location filename="../arpwidget.cpp" line="90"/>
+        <source>Input</source>
+        <translation>Vstup</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="98"/>
+        <location filename="../arpwidget.cpp" line="454"/>
+        <source>Note Filter</source>
+        <translation>Notový filtr</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="99"/>
+        <source>&Note</source>
+        <translation>&Nota</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="156"/>
+        <source>Output</source>
+        <translation>Výstup</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="158"/>
+        <source>&Mute</source>
+        <translation>&Ztlumit</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="164"/>
+        <location filename="../arpwidget.cpp" line="248"/>
+        <source>MIDI &Learn</source>
+        <translation>&Učení se MIDI</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="63"/>
+        <source>Cancel MIDI &Learning</source>
+        <translation>Zrušit &učení se MIDI</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="169"/>
+        <location filename="../arpwidget.cpp" line="253"/>
+        <source>MIDI &Forget</source>
+        <translation>&Zapomenutí nastavení MIDI</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="176"/>
+        <source>&Port</source>
+        <translation>&Přípojka</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="211"/>
+        <source>Pattern</source>
+        <translation>Vzor</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="216"/>
+        <source>&Edit Pattern</source>
+        <translation>&Upravit vzor</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="224"/>
+        <source>&Remove Pattern</source>
+        <translation>&Odstranit vzor</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="232"/>
+        <source>&Store Pattern</source>
+        <translation>&Uložit vzor</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="242"/>
+        <source>Pattern preset</source>
+        <translation>Přednastavení vzoru</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="271"/>
+        <source>No trigger</source>
+        <translation>Přehrát</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="271"/>
+        <source>Kbd restart</source>
+        <translation>Nové spuštění</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="273"/>
+        <source>Trigger Mode</source>
+        <translation>Zůstat stát na nové staccato notě</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="280"/>
+        <source>&Latch Mode</source>
+        <translation>&Držet noty</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="450"/>
+        <source>Note Filter - ACTIVE</source>
+        <translation>Notový filtr - ČINNÝ</translation>
+    </message>
+    <message>
+        <source>Arp through chord</source>
+        <translation type="obsolete">Arpeggio bei Akkord</translation>
+    </message>
+    <message>
+        <source>0..9  note played on keyboard, ascending order
+( ) chord mode on/off
+  + -  octave up/down
+ < . > tempo up/reset/down
+  d h  note length up/down
+  / \  velocity up/down
+   p   pause</source>
+        <translation type="obsolete">0..9 gespielte Note in aufsteigender Anordnung
+( ) Akkord-Modus ein/aus
+ + - Octave auf/ab
+ < . > Tempo auf/ab
+ d h Notenlänge auf/ab
+ / \ Geschwindigkeit auf/ab
+ p Pause</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="378"/>
+        <source>&Attack (s)</source>
+        <translation>&Náběh (s)</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="382"/>
+        <source>&Release (s)</source>
+        <translation>&Uvolnění (s)</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="342"/>
+        <location filename="../arpwidget.cpp" line="860"/>
+        <source>Random</source>
+        <translation>Náhodný</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="73"/>
+        <source>&Rename...</source>
+        <translation>Pře&jmenovat...</translation>
+    </message>
+    <message>
+        <source>Ctrl+R</source>
+        <comment>Module|Rename</comment>
+        <translation type="obsolete">Strg+R</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="74"/>
+        <source>Rename this Arp</source>
+        <translation>Přejmenovat tento arpeggiator</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="79"/>
+        <source>&Delete...</source>
+        <translation>S&mazat...</translation>
+    </message>
+    <message>
+        <source>Ctrl+Del</source>
+        <comment>Module|Delete</comment>
+        <translation type="obsolete">Strg+Del</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="80"/>
+        <source>Delete this Arp</source>
+        <translation>Smazat tento arpeggiator</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="264"/>
+        <source>Repeat mode</source>
+        <translation>Režim opakování</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="271"/>
+        <source>Kbd trigger</source>
+        <translation>Rozběhnout se</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="313"/>
+        <source>0..9  note played on keyboard, 0 is lowest
+( ) numbers in parenthesis are stacked to chords
+  + = -  octave up/reset/down
+ < . > tempo up/reset/down
+  d h  note length up/down
+  / \  velocity up/down
+   p   pause</source>
+        <translation>0..9 přehrávaná nota ve stoupajícím pořadí
+( ) Noty mezi závorkami jsou vykládány jako akkord
+ + = - Oktáva nahoru/běžná hodnota/dolů
+ < . > Tempo nahoru/běžná hodnota/dolů
+ d h Délka noty nahoru/dolů
+ / \ Rychlost nahoru/dolů
+ p Přestávka</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="346"/>
+        <source>&Shift</source>
+        <translation>&Posunutí</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="351"/>
+        <source>Vel&ocity</source>
+        <translation>&Rychlost</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="356"/>
+        <source>&Length</source>
+        <translation>&Délka</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="375"/>
+        <location filename="../arpwidget.cpp" line="886"/>
+        <source>Envelope</source>
+        <translation>Obálka</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="1021"/>
+        <source>Delete "%1"?</source>
+        <translation>Smazat "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="1040"/>
+        <source>New Name</source>
+        <translation>Nový název</translation>
+    </message>
+    <message>
+        <source>Could not write to resource file</source>
+        <translation type="obsolete">Konnte Ressourcendatei nicht speichern</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="798"/>
+        <source>Could not read from resource file</source>
+        <translation>Nepodařilo se číst ze zdrojového souboru</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="856"/>
+        <source>Random - ACTIVE</source>
+        <translation>Náhodný - ČINNÝ</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="882"/>
+        <source>Envelope - ACTIVE</source>
+        <translation>Obálka - ČINNÁ</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="900"/>
+        <source>%1: Store pattern</source>
+        <translation>%1: Uložit vzor</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="901"/>
+        <source>New pattern</source>
+        <translation>Nový vzor</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="901"/>
+        <source>Arp pattern</source>
+        <translation>Vzor pro arpeggiator</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="937"/>
+        <source>Remove "%1"?</source>
+        <translation>Odstranit "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="92"/>
+        <source>&Channel</source>
+        <translation>&Каnál</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="113"/>
+        <source>&Velocity</source>
+        <translation>&Rychlost</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="182"/>
+        <source>C&hannel</source>
+        <translation>Ka&nál</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="262"/>
+        <source>Static</source>
+        <translation>Statický</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="262"/>
+        <source>Up</source>
+        <translation>Nahoru</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="262"/>
+        <source>Down</source>
+        <translation>Dolů</translation>
+    </message>
+</context>
+<context>
+    <name>GrooveWidget</name>
+    <message>
+        <location filename="../groovewidget.cpp" line="45"/>
+        <source>&Shift</source>
+        <translation>&Posunutí</translation>
+    </message>
+    <message>
+        <location filename="../groovewidget.cpp" line="50"/>
+        <source>&Velocity</source>
+        <translation>&Rychlost</translation>
+    </message>
+    <message>
+        <location filename="../groovewidget.cpp" line="55"/>
+        <source>&Length</source>
+        <translation>&Délka</translation>
+    </message>
+</context>
+<context>
+    <name>LfoWidget</name>
+    <message>
+        <location filename="../lfowidget.cpp" line="124"/>
+        <source>Output</source>
+        <translation>Výstup</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="126"/>
+        <source>&Mute</source>
+        <translation>&Ztlumit</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="93"/>
+        <location filename="../lfowidget.cpp" line="146"/>
+        <source>MIDI &CC#</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="152"/>
+        <source>MIDI Controller number sent to output</source>
+        <translation>Číslo ovladače MIDI poslaného do výstupu</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="162"/>
+        <source>C&hannel</source>
+        <translation>&Kanál</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="156"/>
+        <source>&Port</source>
+        <translation>&Přípojka</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="74"/>
+        <source>&Rename...</source>
+        <translation>Pře&jmenovat...</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="75"/>
+        <source>Rename this LFO</source>
+        <translation>Přejmenovat tento nízkokmitočtový oscilátor (LFO)</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="80"/>
+        <source>&Delete...</source>
+        <translation>S&mazat...</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="81"/>
+        <source>Delete this LFO</source>
+        <translation>Smazat tento nízkokmitočtový oscilátor (LFO)</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="91"/>
+        <source>Input</source>
+        <translation>Vstup</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="99"/>
+        <source>MIDI Controller number to record</source>
+        <translation>Číslo ovladače MIDI pro nahrávání</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="104"/>
+        <source>&Channel</source>
+        <translation>&Каnál</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="133"/>
+        <location filename="../lfowidget.cpp" line="225"/>
+        <location filename="../lfowidget.cpp" line="255"/>
+        <location filename="../lfowidget.cpp" line="303"/>
+        <location filename="../lfowidget.cpp" line="322"/>
+        <location filename="../lfowidget.cpp" line="341"/>
+        <source>MIDI &Learn</source>
+        <translation>&Učení se MIDI</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="64"/>
+        <source>Cancel MIDI &Learning</source>
+        <translation>Zrušit &učení se MIDI</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="138"/>
+        <location filename="../lfowidget.cpp" line="230"/>
+        <location filename="../lfowidget.cpp" line="260"/>
+        <location filename="../lfowidget.cpp" line="308"/>
+        <location filename="../lfowidget.cpp" line="327"/>
+        <location filename="../lfowidget.cpp" line="346"/>
+        <source>MIDI &Forget</source>
+        <translation>&Zapomenutí nastavení MIDI</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="196"/>
+        <source>Wave</source>
+        <translation>Vlna</translation>
+    </message>
+    <message>
+        <source>Right button to mute points, left button to draw custom wave</source>
+        <translation type="obsolete">Rechte Maustaste: Stummschalten einzelner Punkte, linke Maustaste: Zeichnen der Wellenform</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="219"/>
+        <source>Waveform Basis</source>
+        <translation>Základ tvaru vlny</translation>
+    </message>
+    <message>
+        <source>&Frequency (cycles/beat)</source>
+        <translation type="obsolete">&Frequenz (Zyklen/Takt)</translation>
+    </message>
+    <message>
+        <source>Frequency: Number of wave cycles produced every beat</source>
+        <translation type="obsolete">Frequenz: Anzahl der erzeugten Wellenzyklen pro beat</translation>
+    </message>
+    <message>
+        <source>&Resolution (events/beat)</source>
+        <translation type="obsolete">&Auflösung (Signale/Takt)</translation>
+    </message>
+    <message>
+        <source>Resolution: Number of events produced every beat</source>
+        <translation type="obsolete">Auflösung: Anzahl der pro Takt erzeugten MIDI Signale</translation>
+    </message>
+    <message>
+        <source>&Length (beats)</source>
+        <translation type="obsolete">Län&ge (Takte)</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="200"/>
+        <source>Right button to mute points
+Left button to draw custom wave
+Wheel to change offset</source>
+        <translation>Pravé tlačítko myši: ztlumení jednotlivých bodů
+Levé tlačítko myši: kreslení tvaru vlny
+Kolečko myši: změna posunu</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="238"/>
+        <source>&Frequency</source>
+        <translation>&Kmitočet</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="248"/>
+        <source>Frequency (cycles/beat): Number of wave cycles produced every beat</source>
+        <translation>Kmitočet (cyklů za dobu): Počet vytvořených vlnových cyklů za čtvrťový takt</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="267"/>
+        <source>&Resolution</source>
+        <translation>&Rozlišení</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="276"/>
+        <source>Resolution (events/beat): Number of events produced every beat</source>
+        <translation>Rozlišení (události (signály) za dobu): Počet signálů MIDI vytvořených za čtvrťový takt</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="281"/>
+        <source>&Length</source>
+        <translation>&Délka</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="288"/>
+        <source>Length of LFO wave in beats</source>
+        <translation>Délka tvaru vlny LFO ve čtvrťových taktech</translation>
+    </message>
+    <message>
+        <source>&Copy to custom wave</source>
+        <translation type="obsolete">&In die freie Wellenform kopieren</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="293"/>
+        <location filename="../lfowidget.cpp" line="294"/>
+        <source>Re&cord</source>
+        <translation>&Nahrát</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="295"/>
+        <source>Record incoming controller</source>
+        <translation>Nahrát příchozí ovladač</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="316"/>
+        <source>&Amplitude</source>
+        <translation>Ro&zkmit</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="336"/>
+        <source>&Offset</source>
+        <translation>Pos&un</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="746"/>
+        <source>Sine</source>
+        <translation>Sinus</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="746"/>
+        <source>Saw up</source>
+        <translation>Pila nahoru</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="746"/>
+        <source>Triangle</source>
+        <translation>Trojúhelník</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="747"/>
+        <source>Saw down</source>
+        <translation>Pila dolů</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="747"/>
+        <source>Square</source>
+        <translation>Čtverec</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="747"/>
+        <source>Custom</source>
+        <translation>Vlastní</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="948"/>
+        <source>Delete "%1"?</source>
+        <translation>Smazat "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="967"/>
+        <source>New Name</source>
+        <translation>Nový název</translation>
+    </message>
+    <message>
+        <source>&MIDI CC#</source>
+        <translation type="obsolete">&MIDI CC#</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="208"/>
+        <source>&Waveform</source>
+        <translation>&Tvar vlny</translation>
+    </message>
+</context>
+<context>
+    <name>LogWidget</name>
+    <message>
+        <location filename="../logwidget.cpp" line="49"/>
+        <source>&Enable Log</source>
+        <translation>&Povolit zápis</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="55"/>
+        <source>Log &MIDI Clock</source>
+        <translation>Zachycovat hodiny &MIDI</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="60"/>
+        <source>&Clear</source>
+        <translation>&Smazat</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="113"/>
+        <source>MIDI Clock</source>
+        <translation>Hodiny MIDI</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="118"/>
+        <source>MIDI Start (Transport)</source>
+        <translation>Začátek hodin MIDI (Transport)</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="122"/>
+        <source>MIDI Continue (Transport)</source>
+        <translation>Pokračování MIDI (Transport)</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="126"/>
+        <source>MIDI Stop (Transport)</source>
+        <translation>Zastavení MIDI (Transport)</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="130"/>
+        <source>Unknown event type</source>
+        <translation>Neznámá událost (signál) MIDI</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <location filename="../mainwindow.cpp" line="81"/>
+        <source>Event Log</source>
+        <translation>Zápis událostí</translation>
+    </message>
+    <message>
+        <source>Settings</source>
+        <translation type="obsolete">Einstellungen</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="96"/>
+        <source>Groove</source>
+        <translation>Groove</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="116"/>
+        <source>&New LFO...</source>
+        <translation>&Nový LFO...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="121"/>
+        <source>&New Sequencer...</source>
+        <translation>&Nový sekvencer...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="122"/>
+        <source>Ctrl+T</source>
+        <comment>Module|New Sequencer</comment>
+        <translation>Ctrl+T</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="123"/>
+        <source>Add new Sequencer to tab bar</source>
+        <translation>Přidat nový sekvencer do pruhu s kartami</translation>
+    </message>
+    <message>
+        <source>&Rename...</source>
+        <translation type="obsolete">&Umbenennen...</translation>
+    </message>
+    <message>
+        <source>Rename this module</source>
+        <translation type="obsolete">Dieses Modul umbenennen</translation>
+    </message>
+    <message>
+        <source>&Delete...</source>
+        <translation type="obsolete">&Löschen...</translation>
+    </message>
+    <message>
+        <source>Delete this module</source>
+        <translation type="obsolete">Dieses Modul löschen</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="127"/>
+        <source>&New</source>
+        <translation>&Nový</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="129"/>
+        <source>Create new arpeggiator file</source>
+        <translation>Vytvořit nový soubor s arpeggiatorem</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="132"/>
+        <source>&Open...</source>
+        <translation>&Otevřít...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="137"/>
+        <source>&Save</source>
+        <translation>&Uložit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="139"/>
+        <source>Save current arpeggiator file</source>
+        <translation>Uložit nynější soubor s arpeggiatorem</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="142"/>
+        <source>Save &as...</source>
+        <translation>Uložit &jako...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="145"/>
+        <source>Save current arpeggiator file with new name</source>
+        <translation>Uložit nynější soubor s arpeggiatorem pod novým názvem</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="148"/>
+        <source>&Quit</source>
+        <translation>&Ukončit</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="149"/>
+        <source>Ctrl+Q</source>
+        <comment>File|Quit</comment>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="150"/>
+        <source>Quit application</source>
+        <translation>Ukončit program</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="153"/>
+        <source>&Run with internal clock</source>
+        <translation>&Spustit/Zastavit s vnitřními hodinami</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="177"/>
+        <source>&Connect to Jack Transport</source>
+        <translation>Spojit s &Jack Transport</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="192"/>
+        <source>Ctrl+H</source>
+        <comment>View|Event Log</comment>
+        <translation>Ctrl+H</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="196"/>
+        <source>&Groove Settings</source>
+        <translation>Nastavení &Groove</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="197"/>
+        <source>Ctrl+G</source>
+        <comment>View|Groove</comment>
+        <translation>Ctrl+G</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="208"/>
+        <source>Mod&ule</source>
+        <translation>Mod&ul</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="214"/>
+        <source>&Recently opened files</source>
+        <translation>Naposledy otevřené sou&bory</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="228"/>
+        <source>&MIDI Controllers...</source>
+        <translation>Ovladače &MIDI...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="241"/>
+        <source>&File Toolbar</source>
+        <translation>Nástrojový pruh pro &soubor</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="249"/>
+        <source>&Control Toolbar</source>
+        <translation>Nástrojový pruh pro &ovládání</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="321"/>
+        <location filename="../mainwindow.cpp" line="334"/>
+        <location filename="../mainwindow.cpp" line="347"/>
+        <source>%1</source>
+        <translation>%1</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="333"/>
+        <source>Add MIDI LFO</source>
+        <translation>Přidat MIDI LFO</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="346"/>
+        <source>Add Step Sequencer</source>
+        <translation>Přidat krokový sekvencer</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="573"/>
+        <source>QMidiArp XML files</source>
+        <translation>Soubory QMidiArp XML</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="574"/>
+        <source>Old QMidiArp files</source>
+        <translation>Soubory textové soubory QMidiArp</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="593"/>
+        <location filename="../mainwindow.cpp" line="749"/>
+        <source>Could not read from file '%1'.</source>
+        <translation>Nepodařilo se číst ze souboru '%1'.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="611"/>
+        <source>This is not a valid xml file for </source>
+        <translation>Toto není platný soubor XML pro </translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="836"/>
+        <source>The QMidiArp text file was imported. If you save this file, it will be saved using the newer xml format under the name
+ '%1'.</source>
+        <translation>Textový soubor QMidiArp byl zaveden. Při novém ukládání bude tento uložen v novém formátu xml pod názvem
+ '%1'.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="862"/>
+        <source>Could not write to file '%1'.</source>
+        <translation>Nepodařilo se zapsat do souboru '%1'.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="968"/>
+        <source>Unnamed file was changed.
+Save changes?</source>
+        <translation>Nepojmenovaný soubor byl změněn.
+Uložit změny?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="970"/>
+        <source>File '%1' was changed.
+Save changes?</source>
+        <translation>Soubor '%1' byl změněn.
+Uložit změny?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="974"/>
+        <source>Save changes</source>
+        <translation>Uložit změny</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1104"/>
+        <source>Could not read from resource file</source>
+        <translation>Nepodařilo se číst ze zdrojového souboru</translation>
+    </message>
+    <message>
+        <source>&Run</source>
+        <translation type="obsolete">&Start</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="168"/>
+        <source>&Use incoming MIDI Clock</source>
+        <translation>&Použít příchozí signál hodin MIDI</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="191"/>
+        <source>&Event Log</source>
+        <translation>&Zápis událostí</translation>
+    </message>
+    <message>
+        <source>Ctrl+L</source>
+        <comment>View|Event Log</comment>
+        <translation type="obsolete">Strg+L</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="199"/>
+        <source>&Settings</source>
+        <translation>&Nastavení</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="201"/>
+        <source>Ctrl+P</source>
+        <comment>View|Settings</comment>
+        <translation>Ctrl+P</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="238"/>
+        <source>&About Qt...</source>
+        <translation>&O Qt...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="306"/>
+        <source>About %1</source>
+        <translation>O %1</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="311"/>
+        <source>About Qt</source>
+        <translation>O Qt</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="320"/>
+        <source>Add MIDI Arpeggiator</source>
+        <translation>Přidat MIDI arpeggiator</translation>
+    </message>
+    <message>
+        <source>New Name</source>
+        <translation type="obsolete">Neuer Name</translation>
+    </message>
+    <message>
+        <source>Remove "%1"?</source>
+        <translation type="obsolete">"%1" löschen?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="134"/>
+        <location filename="../mainwindow.cpp" line="572"/>
+        <source>Open arpeggiator file</source>
+        <translation>Otevřít soubor s arpeggiatorem</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="163"/>
+        <source>Tempo of internal clock</source>
+        <translation>Tempo vnitřních hodin</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="609"/>
+        <source>Not a QMidiArp xml file.</source>
+        <translation>Není souborem XML QMidiArp.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="946"/>
+        <source>Save arpeggiator</source>
+        <translation>Uložit arpeggiator</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="946"/>
+        <source>QMidiArp files</source>
+        <translation>Soubory QMidiArp</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1147"/>
+        <source>Could not write to resource file</source>
+        <translation>Nepodařilo se zapsat do zdrojového souboru</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="111"/>
+        <source>&New Arp...</source>
+        <translation>&Nový arpeggiator...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="113"/>
+        <source>Add new arpeggiator to tab bar</source>
+        <translation>Přidat nový arpeggiator do pruhu s kartami</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="118"/>
+        <source>Add new LFO to tab bar</source>
+        <translation>Přidat nový LFO do pruhu s kartami</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="112"/>
+        <source>Ctrl+A</source>
+        <comment>Module|New Arp</comment>
+        <translation>Ctrl+A</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="117"/>
+        <source>Ctrl+L</source>
+        <comment>Module|New LFO</comment>
+        <translation>Ctrl+L</translation>
+    </message>
+    <message>
+        <source>Ctrl+R</source>
+        <comment>Module|Rename</comment>
+        <translation type="obsolete">Strg+R</translation>
+    </message>
+    <message>
+        <source>Ctrl+Del</source>
+        <comment>Module|Delete</comment>
+        <translation type="obsolete">Strg+Del</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="206"/>
+        <source>&File</source>
+        <translation>&Soubor</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="207"/>
+        <source>&View</source>
+        <translation>&Pohled</translation>
+    </message>
+    <message>
+        <source>&Module</source>
+        <translation type="obsolete">&Modul</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="209"/>
+        <source>&Help</source>
+        <translation>&Nápověda</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="236"/>
+        <source>&About %1...</source>
+        <translation>&O %1...</translation>
+    </message>
+</context>
+<context>
+    <name>MidiCCTable</name>
+    <message>
+        <location filename="../midicctable.cpp" line="48"/>
+        <source>Re&move</source>
+        <translation>&Odstranit</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="49"/>
+        <source>Re&vert</source>
+        <translation>&Vrátit</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="62"/>
+        <source>MIDI Controllers - </source>
+        <translation>Ovladače MIDI - </translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="117"/>
+        <source>Control</source>
+        <translation>Ovládání</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="121"/>
+        <source>CC#</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="125"/>
+        <source>Ch</source>
+        <translation>Kanál</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="129"/>
+        <source>min</source>
+        <translation>Min</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="133"/>
+        <source>max</source>
+        <translation>Max</translation>
+    </message>
+</context>
+<context>
+    <name>PassWidget</name>
+    <message>
+        <location filename="../passwidget.cpp" line="40"/>
+        <source>&Forward unmatched events to port</source>
+        <translation>&Neodpovídající události (signály MIDI) vést dál na přípojku</translation>
+    </message>
+    <message>
+        <location filename="../passwidget.cpp" line="63"/>
+        <source>&Compact module layout style</source>
+        <translation>&Kompaktní styl rozvržení pro moduly</translation>
+    </message>
+    <message>
+        <location filename="../passwidget.cpp" line="57"/>
+        <source>&Modules controllable by MIDI controller</source>
+        <translation>&Moduly jsou říditelné ovladačem MIDI</translation>
+    </message>
+    <message>
+        <location filename="../passwidget.cpp" line="82"/>
+        <source>Settings - </source>
+        <translation>Nastavení - </translation>
+    </message>
+    <message>
+        <source>&Modules mutable by MIDI controller starting at CC#</source>
+        <translation type="obsolete">&Module stummschaltbar durch MIDI controller beginnend mit CC#</translation>
+    </message>
+    <message>
+        <source>&Arps mutable by MIDI CC starting at CC#</source>
+        <translation type="obsolete">&Arps stummschaltbar durch MIDI CC beginnend mit CC#</translation>
+    </message>
+    <message>
+        <source>Incoming MIDI &Clock rate (tpb)</source>
+        <translation type="obsolete">Kadenz der eingehenden MIDI &Clock (tpb)</translation>
+    </message>
+</context>
+<context>
+    <name>SeqWidget</name>
+    <message>
+        <location filename="../seqwidget.cpp" line="92"/>
+        <source>Transpose the sequence following incoming notes</source>
+        <translation>Převést sekvenci skrz příchozí noty</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="162"/>
+        <source>Output</source>
+        <translation>Výstup</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="164"/>
+        <source>&Mute</source>
+        <translation>&Ztlumit</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="170"/>
+        <location filename="../seqwidget.cpp" line="253"/>
+        <location filename="../seqwidget.cpp" line="314"/>
+        <location filename="../seqwidget.cpp" line="333"/>
+        <source>MIDI &Learn</source>
+        <translation>&Učení se MIDI</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="59"/>
+        <source>Cancel MIDI &Learning</source>
+        <translation>Zrušit &učení se MIDI</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="94"/>
+        <source>&Note Off</source>
+        <translation>&Nota vypnuta</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="98"/>
+        <source>Stop output when Note is released</source>
+        <translation>Zastavit výstup, když je nota vydána</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="106"/>
+        <source>&Restart</source>
+        <translation>&Spustit znovu</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="110"/>
+        <source>Restart sequence when a new note is received</source>
+        <translation>Spustit sekvenci znovu, když je nota přijata</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="112"/>
+        <source>&Trigger</source>
+        <translation>&Spoušť</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="116"/>
+        <source>Retrigger sequence when a new note is received</source>
+        <translation>Spustit sekvenci znovu, když je přijata nová nota</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="118"/>
+        <source>&Loop</source>
+        <translation>&Smyčka</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="122"/>
+        <source>Play sequence as loop instead of a single run</source>
+        <translation>Přehrávat sekvenci ve smyčce namísto jednoho přehrání</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="175"/>
+        <location filename="../seqwidget.cpp" line="258"/>
+        <location filename="../seqwidget.cpp" line="319"/>
+        <location filename="../seqwidget.cpp" line="338"/>
+        <source>MIDI &Forget</source>
+        <translation>&Zapomenutí nastavení MIDI</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="221"/>
+        <source>Sequence</source>
+        <translation>Sekvence</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="232"/>
+        <source>&Sequence</source>
+        <translation>&Sekvence</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="238"/>
+        <source>Preset Number</source>
+        <translation>Číslo přednastavení</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="243"/>
+        <location filename="../seqwidget.cpp" line="244"/>
+        <source>Re&cord</source>
+        <translation>&Nahrát</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="245"/>
+        <source>Record step by step</source>
+        <translation>Příchozí noty nahrávat krok za krokem</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="275"/>
+        <source>Resolution (notes/beat): Number of notes produced every beat</source>
+        <translation>Rozlišení (not za dobu): Počet not zahraných za čtvrťový takt</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="287"/>
+        <source>Length of Sequence in beats</source>
+        <translation>Délka sekvence ve čtvrťových taktech</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="294"/>
+        <source>C&opy to new wave</source>
+        <translation>&Kopírovat do nové vlny</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="308"/>
+        <source>Veloc&ity</source>
+        <translation>&Rychlost</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="966"/>
+        <source>Delete "%1"?</source>
+        <translation>Smazat "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="985"/>
+        <source>New Name</source>
+        <translation>Nový název</translation>
+    </message>
+    <message>
+        <source>&Copy to new wave</source>
+        <translation type="obsolete">In neue Wellenform &kopieren</translation>
+    </message>
+    <message>
+        <source>Velo&city</source>
+        <translation type="obsolete">An&schlag</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="328"/>
+        <source>N&ote Length</source>
+        <translation>Délka n&oty</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="100"/>
+        <source>&Velocity</source>
+        <translation>&Rychlost</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="69"/>
+        <source>&Rename...</source>
+        <translation>Pře&jmenovat...</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="70"/>
+        <source>Rename this Sequencer</source>
+        <translation>Přejmenovat tento sekvencer</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="75"/>
+        <source>&Delete...</source>
+        <translation>S&mazat...</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="76"/>
+        <source>Delete this Sequencer</source>
+        <translation>Smazat tento sekvencer</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="86"/>
+        <source>Input</source>
+        <translation>Vstup</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="88"/>
+        <source>&Note</source>
+        <translation>&Nota</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="104"/>
+        <source>Set sequence velocity to that of incoming notes</source>
+        <translation>Nastavit rychlost sekvence na rychlost příchozích not</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="131"/>
+        <source>&Channel</source>
+        <translation>&Каnál</translation>
+    </message>
+    <message>
+        <source>&Note Length</source>
+        <translation type="obsolete">&Notenlänge</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="346"/>
+        <source>&Transpose</source>
+        <translation>&Převést</translation>
+    </message>
+    <message>
+        <source>&MIDI CC#</source>
+        <translation type="obsolete">&MIDI CC#</translation>
+    </message>
+    <message>
+        <source>MIDI Controller number sent to output</source>
+        <translation type="obsolete">Nummer des gesendeten MIDI Controllers</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="183"/>
+        <source>&Port</source>
+        <translation>&Přípojka</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="189"/>
+        <source>C&hannel</source>
+        <translation>&Kanál</translation>
+    </message>
+    <message>
+        <source>Wave</source>
+        <translation type="obsolete">Welle</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="225"/>
+        <source>Right button to mute points, left button to draw custom wave</source>
+        <translation>Pravé tlačítko myši: ztlumení jednotlivých bodů, levé tlačítko myši: kreslení tvaru vlny</translation>
+    </message>
+    <message>
+        <source>&Waveform</source>
+        <translation type="obsolete">&Wellenform</translation>
+    </message>
+    <message>
+        <source>Waveform Basis</source>
+        <translation type="obsolete">Wellenformbasis</translation>
+    </message>
+    <message>
+        <source>&Frequency</source>
+        <translation type="obsolete">&Frequenz</translation>
+    </message>
+    <message>
+        <source>Frequency (cycles/beat): Number of wave cycles produced every beat</source>
+        <translation type="obsolete">Frequenz (Zyklen pro beat): Anzahl der erzeugten Wellenzyklen pro Vierteltakt</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="265"/>
+        <source>&Resolution</source>
+        <translation>&Rozlišení</translation>
+    </message>
+    <message>
+        <source>Resolution (events/beat): Number of events produced every beat</source>
+        <translation type="obsolete">Auflösung (Signale/beat): Anzahl der pro Vierteltakt erzeugten MIDI Signale</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="280"/>
+        <source>&Length</source>
+        <translation>&Délka</translation>
+    </message>
+    <message>
+        <source>&Copy to custom wave</source>
+        <translation type="obsolete">&In die freie Wellenform kopieren</translation>
+    </message>
+    <message>
+        <source>&Amplitude</source>
+        <translation type="obsolete">Am&plitude</translation>
+    </message>
+    <message>
+        <source>&Offset</source>
+        <translation type="obsolete">&Offset</translation>
+    </message>
+    <message>
+        <source>Sine</source>
+        <translation type="obsolete">Sinus</translation>
+    </message>
+    <message>
+        <source>Saw up</source>
+        <translation type="obsolete">Sägezahn auf</translation>
+    </message>
+    <message>
+        <source>Triangle</source>
+        <translation type="obsolete">Dreieck</translation>
+    </message>
+    <message>
+        <source>Saw down</source>
+        <translation type="obsolete">Sägezahn ab</translation>
+    </message>
+    <message>
+        <source>Square</source>
+        <translation type="obsolete">Rechteck</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="757"/>
+        <source>Custom</source>
+        <translation>Vlastní</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/qmidiarp_de.ts b/src/translations/qmidiarp_de.ts
index d77311d..3e2ffc1 100644
--- a/src/translations/qmidiarp_de.ts
+++ b/src/translations/qmidiarp_de.ts
@@ -4,101 +4,100 @@
 <context>
     <name>ArpWidget</name>
     <message>
-        <location filename="../arpwidget.cpp" line="49"/>
+        <location filename="../arpwidget.cpp" line="90"/>
         <source>Input</source>
         <translation>Eingang</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="58"/>
-        <location filename="../arpwidget.cpp" line="414"/>
+        <location filename="../arpwidget.cpp" line="98"/>
+        <location filename="../arpwidget.cpp" line="454"/>
         <source>Note Filter</source>
         <translation>Notenfilter</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="59"/>
+        <location filename="../arpwidget.cpp" line="99"/>
         <source>&Note</source>
         <translation>&Note</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="116"/>
+        <location filename="../arpwidget.cpp" line="156"/>
         <source>Output</source>
         <translation>Ausgang</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="118"/>
+        <location filename="../arpwidget.cpp" line="158"/>
         <source>&Mute</source>
         <translation>&Stumm</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="122"/>
-        <location filename="../arpwidget.cpp" line="210"/>
+        <location filename="../arpwidget.cpp" line="164"/>
+        <location filename="../arpwidget.cpp" line="248"/>
         <source>MIDI &Learn</source>
         <translation>&Lerne von MIDI</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="125"/>
-        <location filename="../arpwidget.cpp" line="213"/>
+        <location filename="../arpwidget.cpp" line="63"/>
         <source>Cancel MIDI &Learning</source>
         <translation>MIDI-Lernen &Abbrechen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="129"/>
-        <location filename="../arpwidget.cpp" line="217"/>
+        <location filename="../arpwidget.cpp" line="169"/>
+        <location filename="../arpwidget.cpp" line="253"/>
         <source>MIDI &Forget</source>
         <translation>MIDI-Steuerungen &vergessen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="136"/>
+        <location filename="../arpwidget.cpp" line="176"/>
         <source>&Port</source>
         <translation>Anschl&uss</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="173"/>
+        <location filename="../arpwidget.cpp" line="211"/>
         <source>Pattern</source>
         <translation>Muster</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="178"/>
+        <location filename="../arpwidget.cpp" line="216"/>
         <source>&Edit Pattern</source>
         <translation>&Bearbeiten</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="186"/>
+        <location filename="../arpwidget.cpp" line="224"/>
         <source>&Remove Pattern</source>
         <translation>&Löschen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="194"/>
+        <location filename="../arpwidget.cpp" line="232"/>
         <source>&Store Pattern</source>
         <translation>&Speichern</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="204"/>
+        <location filename="../arpwidget.cpp" line="242"/>
         <source>Pattern preset</source>
         <translation>Mustervorlage</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="232"/>
+        <location filename="../arpwidget.cpp" line="271"/>
         <source>No trigger</source>
         <translation>Durchspielen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="232"/>
+        <location filename="../arpwidget.cpp" line="271"/>
         <source>Kbd restart</source>
         <translation>Neustart</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="234"/>
+        <location filename="../arpwidget.cpp" line="273"/>
         <source>Trigger Mode</source>
         <translation>Verhalten bei neuer Stakato-Note</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="241"/>
+        <location filename="../arpwidget.cpp" line="280"/>
         <source>&Latch Mode</source>
         <translation>&Noten ha&lten</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="410"/>
+        <location filename="../arpwidget.cpp" line="450"/>
         <source>Note Filter - ACTIVE</source>
         <translation>Notenfilter - AKTIV</translation>
     </message>
@@ -123,23 +122,23 @@
  p Pause</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="339"/>
+        <location filename="../arpwidget.cpp" line="378"/>
         <source>&Attack (s)</source>
         <translation>A&ttack (s)</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="343"/>
+        <location filename="../arpwidget.cpp" line="382"/>
         <source>&Release (s)</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="303"/>
-        <location filename="../arpwidget.cpp" line="865"/>
+        <location filename="../arpwidget.cpp" line="342"/>
+        <location filename="../arpwidget.cpp" line="860"/>
         <source>Random</source>
         <translation>Zufall</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="32"/>
+        <location filename="../arpwidget.cpp" line="73"/>
         <source>&Rename...</source>
         <translation>&Umbenennen...</translation>
     </message>
@@ -149,12 +148,12 @@
         <translation type="obsolete">Strg+R</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="33"/>
+        <location filename="../arpwidget.cpp" line="74"/>
         <source>Rename this Arp</source>
         <translation>Diesen arpeggiator umbenennen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="38"/>
+        <location filename="../arpwidget.cpp" line="79"/>
         <source>&Delete...</source>
         <translation>&Löschen...</translation>
     </message>
@@ -164,22 +163,22 @@
         <translation type="obsolete">Strg+Del</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="39"/>
+        <location filename="../arpwidget.cpp" line="80"/>
         <source>Delete this Arp</source>
         <translation>Diesen Arpeggiator löschen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="225"/>
+        <location filename="../arpwidget.cpp" line="264"/>
         <source>Repeat mode</source>
         <translation>Wiederholungsmodus</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="232"/>
+        <location filename="../arpwidget.cpp" line="271"/>
         <source>Kbd trigger</source>
         <translation>Loslaufen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="274"/>
+        <location filename="../arpwidget.cpp" line="313"/>
         <source>0..9  note played on keyboard, 0 is lowest
 ( ) numbers in parenthesis are stacked to chords
   + = -  octave up/reset/down
@@ -196,33 +195,33 @@
  p Pause</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="307"/>
+        <location filename="../arpwidget.cpp" line="346"/>
         <source>&Shift</source>
         <translation>&Verschiebung</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="312"/>
+        <location filename="../arpwidget.cpp" line="351"/>
         <source>Vel&ocity</source>
         <translation>Ans&chlag</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="317"/>
+        <location filename="../arpwidget.cpp" line="356"/>
         <source>&Length</source>
         <translation>&Dauer</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="336"/>
-        <location filename="../arpwidget.cpp" line="891"/>
+        <location filename="../arpwidget.cpp" line="375"/>
+        <location filename="../arpwidget.cpp" line="886"/>
         <source>Envelope</source>
         <translation>Hüllkurve</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="990"/>
+        <location filename="../arpwidget.cpp" line="1021"/>
         <source>Delete "%1"?</source>
         <translation>"%1" löschen?</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="1009"/>
+        <location filename="../arpwidget.cpp" line="1040"/>
         <source>New Name</source>
         <translation>Neuer Name</translation>
     </message>
@@ -231,67 +230,67 @@
         <translation type="obsolete">Konnte Ressourcendatei nicht speichern</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="803"/>
+        <location filename="../arpwidget.cpp" line="798"/>
         <source>Could not read from resource file</source>
         <translation>Konnte Ressourcendatei nicht einlesen</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="861"/>
+        <location filename="../arpwidget.cpp" line="856"/>
         <source>Random - ACTIVE</source>
         <translation>Zufall - AKTIV</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="887"/>
+        <location filename="../arpwidget.cpp" line="882"/>
         <source>Envelope - ACTIVE</source>
         <translation>Hüllkurve - AKTIV</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="905"/>
+        <location filename="../arpwidget.cpp" line="900"/>
         <source>%1: Store pattern</source>
         <translation>%1: Muster speichern</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="906"/>
+        <location filename="../arpwidget.cpp" line="901"/>
         <source>New pattern</source>
         <translation>Neues Muster</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="906"/>
+        <location filename="../arpwidget.cpp" line="901"/>
         <source>Arp pattern</source>
         <translation>Arp-Muster</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="942"/>
+        <location filename="../arpwidget.cpp" line="937"/>
         <source>Remove "%1"?</source>
         <translation>"%1" löschen?</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="51"/>
+        <location filename="../arpwidget.cpp" line="92"/>
         <source>&Channel</source>
         <translation>&Kanal</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="73"/>
+        <location filename="../arpwidget.cpp" line="113"/>
         <source>&Velocity</source>
         <translation>&Anschlag</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="143"/>
+        <location filename="../arpwidget.cpp" line="182"/>
         <source>C&hannel</source>
         <translation>Kana&l</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="223"/>
+        <location filename="../arpwidget.cpp" line="262"/>
         <source>Static</source>
         <translation>Statisch</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="223"/>
+        <location filename="../arpwidget.cpp" line="262"/>
         <source>Up</source>
         <translation>Aufwärts</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="223"/>
+        <location filename="../arpwidget.cpp" line="262"/>
         <source>Down</source>
         <translation>Abwärts</translation>
     </message>
@@ -299,17 +298,17 @@
 <context>
     <name>GrooveWidget</name>
     <message>
-        <location filename="../groovewidget.cpp" line="20"/>
+        <location filename="../groovewidget.cpp" line="45"/>
         <source>&Shift</source>
         <translation>&Verschiebung</translation>
     </message>
     <message>
-        <location filename="../groovewidget.cpp" line="25"/>
+        <location filename="../groovewidget.cpp" line="50"/>
         <source>&Velocity</source>
         <translation>&Anschlag</translation>
     </message>
     <message>
-        <location filename="../groovewidget.cpp" line="30"/>
+        <location filename="../groovewidget.cpp" line="55"/>
         <source>&Length</source>
         <translation>&Dauer</translation>
     </message>
@@ -317,80 +316,98 @@
 <context>
     <name>LfoWidget</name>
     <message>
-        <location filename="../lfowidget.cpp" line="68"/>
+        <location filename="../lfowidget.cpp" line="124"/>
         <source>Output</source>
         <translation>Ausgang</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="70"/>
+        <location filename="../lfowidget.cpp" line="126"/>
         <source>&Mute</source>
         <translation>&Stumm</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="92"/>
+        <location filename="../lfowidget.cpp" line="93"/>
+        <location filename="../lfowidget.cpp" line="146"/>
         <source>MIDI &CC#</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="98"/>
+        <location filename="../lfowidget.cpp" line="152"/>
         <source>MIDI Controller number sent to output</source>
         <translation>Nummer des gesendeten MIDI Controllers</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="109"/>
+        <location filename="../lfowidget.cpp" line="162"/>
         <source>C&hannel</source>
         <translation>&Kanal</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="102"/>
+        <location filename="../lfowidget.cpp" line="156"/>
         <source>&Port</source>
         <translation>An&schluss</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="51"/>
+        <location filename="../lfowidget.cpp" line="74"/>
         <source>&Rename...</source>
         <translation>&Umbenennen...</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="52"/>
+        <location filename="../lfowidget.cpp" line="75"/>
         <source>Rename this LFO</source>
         <translation>Diesen LFO umbenennen</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="57"/>
+        <location filename="../lfowidget.cpp" line="80"/>
         <source>&Delete...</source>
         <translation>&Löschen...</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="58"/>
+        <location filename="../lfowidget.cpp" line="81"/>
         <source>Delete this LFO</source>
         <translation>Diesen LFO löschen</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="79"/>
-        <location filename="../lfowidget.cpp" line="166"/>
-        <location filename="../lfowidget.cpp" line="192"/>
-        <location filename="../lfowidget.cpp" line="232"/>
-        <location filename="../lfowidget.cpp" line="249"/>
+        <location filename="../lfowidget.cpp" line="91"/>
+        <source>Input</source>
+        <translation>Eingang</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="99"/>
+        <source>MIDI Controller number to record</source>
+        <translation>Die aufzunehmende MIDI Controller ID</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="104"/>
+        <source>&Channel</source>
+        <translation>&Kanal</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="133"/>
+        <location filename="../lfowidget.cpp" line="225"/>
+        <location filename="../lfowidget.cpp" line="255"/>
+        <location filename="../lfowidget.cpp" line="303"/>
+        <location filename="../lfowidget.cpp" line="322"/>
+        <location filename="../lfowidget.cpp" line="341"/>
         <source>MIDI &Learn</source>
         <translation>&Lerne von MIDI</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="73"/>
+        <location filename="../lfowidget.cpp" line="64"/>
         <source>Cancel MIDI &Learning</source>
         <translation>MIDI-Lernen &Abbrechen</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="82"/>
-        <location filename="../lfowidget.cpp" line="169"/>
-        <location filename="../lfowidget.cpp" line="195"/>
-        <location filename="../lfowidget.cpp" line="235"/>
-        <location filename="../lfowidget.cpp" line="252"/>
+        <location filename="../lfowidget.cpp" line="138"/>
+        <location filename="../lfowidget.cpp" line="230"/>
+        <location filename="../lfowidget.cpp" line="260"/>
+        <location filename="../lfowidget.cpp" line="308"/>
+        <location filename="../lfowidget.cpp" line="327"/>
+        <location filename="../lfowidget.cpp" line="346"/>
         <source>MIDI &Forget</source>
         <translation>MIDI-Steuerungen &vergessen</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="137"/>
+        <location filename="../lfowidget.cpp" line="196"/>
         <source>Wave</source>
         <translation>Welle</translation>
     </message>
@@ -399,7 +416,7 @@
         <translation type="obsolete">Rechte Maustaste: Stummschalten einzelner Punkte, linke Maustaste: Zeichnen der Wellenform</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="160"/>
+        <location filename="../lfowidget.cpp" line="219"/>
         <source>Waveform Basis</source>
         <translation>Wellenformbasis</translation>
     </message>
@@ -424,7 +441,7 @@
         <translation type="obsolete">Län&ge (Takte)</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="141"/>
+        <location filename="../lfowidget.cpp" line="200"/>
         <source>Right button to mute points
 Left button to draw custom wave
 Wheel to change offset</source>
@@ -433,32 +450,32 @@ Linke Maustaste: Zeichnen der Wellenform
 Mausrad: Verschieben des Offsets</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="175"/>
+        <location filename="../lfowidget.cpp" line="238"/>
         <source>&Frequency</source>
         <translation>&Frequenz</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="185"/>
+        <location filename="../lfowidget.cpp" line="248"/>
         <source>Frequency (cycles/beat): Number of wave cycles produced every beat</source>
         <translation>Frequenz (Zyklen pro beat): Anzahl der erzeugten Wellenzyklen pro Vierteltakt</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="201"/>
+        <location filename="../lfowidget.cpp" line="267"/>
         <source>&Resolution</source>
         <translation>&Auflösung</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="210"/>
+        <location filename="../lfowidget.cpp" line="276"/>
         <source>Resolution (events/beat): Number of events produced every beat</source>
         <translation>Auflösung (Signale/beat): Anzahl der pro Vierteltakt erzeugten MIDI Signale</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="215"/>
+        <location filename="../lfowidget.cpp" line="281"/>
         <source>&Length</source>
         <translation>&Dauer</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="222"/>
+        <location filename="../lfowidget.cpp" line="288"/>
         <source>Length of LFO wave in beats</source>
         <translation>Länge der LFO Wellenform in Vierteltakten</translation>
     </message>
@@ -467,52 +484,63 @@ Mausrad: Verschieben des Offsets</translation>
         <translation type="obsolete">&In die freie Wellenform kopieren</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="228"/>
+        <location filename="../lfowidget.cpp" line="293"/>
+        <location filename="../lfowidget.cpp" line="294"/>
+        <source>Re&cord</source>
+        <translation>&Aufnahme</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="295"/>
+        <source>Record incoming controller</source>
+        <translation>Eingehenden MIDI controller aufzeichnen</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="316"/>
         <source>&Amplitude</source>
         <translation>Am&plitude</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="245"/>
+        <location filename="../lfowidget.cpp" line="336"/>
         <source>&Offset</source>
         <translation>&Offset</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="668"/>
+        <location filename="../lfowidget.cpp" line="746"/>
         <source>Sine</source>
         <translation>Sinus</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="668"/>
+        <location filename="../lfowidget.cpp" line="746"/>
         <source>Saw up</source>
         <translation>Sägezahn auf</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="668"/>
+        <location filename="../lfowidget.cpp" line="746"/>
         <source>Triangle</source>
         <translation>Dreieck</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="669"/>
+        <location filename="../lfowidget.cpp" line="747"/>
         <source>Saw down</source>
         <translation>Sägezahn ab</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="669"/>
+        <location filename="../lfowidget.cpp" line="747"/>
         <source>Square</source>
         <translation>Rechteck</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="669"/>
+        <location filename="../lfowidget.cpp" line="747"/>
         <source>Custom</source>
         <translation>Frei</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="842"/>
+        <location filename="../lfowidget.cpp" line="948"/>
         <source>Delete "%1"?</source>
         <translation>"%1" löschen?</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="861"/>
+        <location filename="../lfowidget.cpp" line="967"/>
         <source>New Name</source>
         <translation>Neuer Name</translation>
     </message>
@@ -521,7 +549,7 @@ Mausrad: Verschieben des Offsets</translation>
         <translation type="obsolete">&MIDI CC#</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="149"/>
+        <location filename="../lfowidget.cpp" line="208"/>
         <source>&Waveform</source>
         <translation>&Wellenform</translation>
     </message>
@@ -529,42 +557,42 @@ Mausrad: Verschieben des Offsets</translation>
 <context>
     <name>LogWidget</name>
     <message>
-        <location filename="../logwidget.cpp" line="27"/>
+        <location filename="../logwidget.cpp" line="49"/>
         <source>&Enable Log</source>
         <translation>&Protokollieren</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="33"/>
+        <location filename="../logwidget.cpp" line="55"/>
         <source>Log &MIDI Clock</source>
         <translation>&MIDI Clock aufzeichnen</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="38"/>
+        <location filename="../logwidget.cpp" line="60"/>
         <source>&Clear</source>
         <translation>&Löschen</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="91"/>
+        <location filename="../logwidget.cpp" line="113"/>
         <source>MIDI Clock</source>
         <translation>MIDI Clock</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="96"/>
+        <location filename="../logwidget.cpp" line="118"/>
         <source>MIDI Start (Transport)</source>
         <translation>MIDI Clock Start (Transport)</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="100"/>
+        <location filename="../logwidget.cpp" line="122"/>
         <source>MIDI Continue (Transport)</source>
         <translation>MIDI Continue (Transport)</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="104"/>
+        <location filename="../logwidget.cpp" line="126"/>
         <source>MIDI Stop (Transport)</source>
         <translation>MIDI Stop (Transport)</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="108"/>
+        <location filename="../logwidget.cpp" line="130"/>
         <source>Unknown event type</source>
         <translation>Unbekanntes MIDI-Signal</translation>
     </message>
@@ -572,7 +600,7 @@ Mausrad: Verschieben des Offsets</translation>
 <context>
     <name>MainWindow</name>
     <message>
-        <location filename="../mainwindow.cpp" line="58"/>
+        <location filename="../mainwindow.cpp" line="81"/>
         <source>Event Log</source>
         <translation>Protokoll</translation>
     </message>
@@ -581,28 +609,28 @@ Mausrad: Verschieben des Offsets</translation>
         <translation type="obsolete">Einstellungen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="73"/>
+        <location filename="../mainwindow.cpp" line="96"/>
         <source>Groove</source>
         <translation>Groove</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="93"/>
+        <location filename="../mainwindow.cpp" line="116"/>
         <source>&New LFO...</source>
         <translation>&Neuer LFO...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="98"/>
+        <location filename="../mainwindow.cpp" line="121"/>
         <source>&New Sequencer...</source>
         <translation>&Neuer Sequenzer...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="99"/>
+        <location filename="../mainwindow.cpp" line="122"/>
         <source>Ctrl+T</source>
         <comment>Module|New Sequencer</comment>
         <translation>Strg+T</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="100"/>
+        <location filename="../mainwindow.cpp" line="123"/>
         <source>Add new Sequencer to tab bar</source>
         <translation>Neuen Sequenzer hinzufügen</translation>
     </message>
@@ -623,180 +651,179 @@ Mausrad: Verschieben des Offsets</translation>
         <translation type="obsolete">Dieses Modul löschen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="104"/>
+        <location filename="../mainwindow.cpp" line="127"/>
         <source>&New</source>
         <translation>&Neu</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="106"/>
+        <location filename="../mainwindow.cpp" line="129"/>
         <source>Create new arpeggiator file</source>
         <translation>Neue Arpeggiator-Zusammenstellung erzeugen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="109"/>
+        <location filename="../mainwindow.cpp" line="132"/>
         <source>&Open...</source>
         <translation>&Öffnen...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="114"/>
+        <location filename="../mainwindow.cpp" line="137"/>
         <source>&Save</source>
         <translation>&Speichern</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="116"/>
+        <location filename="../mainwindow.cpp" line="139"/>
         <source>Save current arpeggiator file</source>
         <translation>Aktuelle Arpeggiator-Zusammenstellung speichern</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="119"/>
+        <location filename="../mainwindow.cpp" line="142"/>
         <source>Save &as...</source>
         <translation>Speichern &unter...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="122"/>
+        <location filename="../mainwindow.cpp" line="145"/>
         <source>Save current arpeggiator file with new name</source>
         <translation>Aktuelle Arpeggiator-Zusammenstellung unter neuem Namen speichern</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="125"/>
+        <location filename="../mainwindow.cpp" line="148"/>
         <source>&Quit</source>
         <translation>&Beenden</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="126"/>
+        <location filename="../mainwindow.cpp" line="149"/>
         <source>Ctrl+Q</source>
         <comment>File|Quit</comment>
         <translation>Strg+Q</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="127"/>
+        <location filename="../mainwindow.cpp" line="150"/>
         <source>Quit application</source>
         <translation>Die Anwendung beenden</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="130"/>
+        <location filename="../mainwindow.cpp" line="153"/>
         <source>&Run with internal clock</source>
         <translation>&Start/Stop mit interner Uhr</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="154"/>
+        <location filename="../mainwindow.cpp" line="177"/>
         <source>&Connect to Jack Transport</source>
         <translation>Mit &Jack Transport verbinden</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="169"/>
+        <location filename="../mainwindow.cpp" line="192"/>
         <source>Ctrl+H</source>
         <comment>View|Event Log</comment>
         <translation>Strg+H</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="173"/>
+        <location filename="../mainwindow.cpp" line="196"/>
         <source>&Groove Settings</source>
         <translation>&Groove</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="174"/>
+        <location filename="../mainwindow.cpp" line="197"/>
         <source>Ctrl+G</source>
         <comment>View|Groove</comment>
         <translation>Strg+G</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="185"/>
+        <location filename="../mainwindow.cpp" line="208"/>
         <source>Mod&ule</source>
         <translation>Mod&ul</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="191"/>
+        <location filename="../mainwindow.cpp" line="214"/>
         <source>&Recently opened files</source>
         <translation>&Zuletzt geöffnete Dateien</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="205"/>
+        <location filename="../mainwindow.cpp" line="228"/>
         <source>&MIDI Controllers...</source>
         <translation>&MIDI-Steuerungen...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="218"/>
+        <location filename="../mainwindow.cpp" line="241"/>
         <source>&File Toolbar</source>
         <translation>&Dateien Werkzeugleiste</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="226"/>
+        <location filename="../mainwindow.cpp" line="249"/>
         <source>&Control Toolbar</source>
         <translation>&Steuerungsleiste</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="299"/>
-        <location filename="../mainwindow.cpp" line="312"/>
-        <location filename="../mainwindow.cpp" line="325"/>
+        <location filename="../mainwindow.cpp" line="321"/>
+        <location filename="../mainwindow.cpp" line="334"/>
+        <location filename="../mainwindow.cpp" line="347"/>
         <source>%1</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="311"/>
+        <location filename="../mainwindow.cpp" line="333"/>
         <source>Add MIDI LFO</source>
         <translation>MIDI LFO hinzufügen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="324"/>
+        <location filename="../mainwindow.cpp" line="346"/>
         <source>Add Step Sequencer</source>
         <translation>Sequenzer hinzufügen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="547"/>
+        <location filename="../mainwindow.cpp" line="573"/>
         <source>QMidiArp XML files</source>
         <translation>QMidiArp XML Dateien</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="548"/>
+        <location filename="../mainwindow.cpp" line="574"/>
         <source>Old QMidiArp files</source>
         <translation>Alte QMidiArp Textdateien</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="567"/>
-        <location filename="../mainwindow.cpp" line="723"/>
+        <location filename="../mainwindow.cpp" line="593"/>
+        <location filename="../mainwindow.cpp" line="749"/>
         <source>Could not read from file '%1'.</source>
         <translation>Konnte die Datei '%1' nicht lesen.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="585"/>
+        <location filename="../mainwindow.cpp" line="611"/>
         <source>This is not a valid xml file for </source>
         <translation>Dies ist keine gültige xml Datei für </translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="810"/>
+        <location filename="../mainwindow.cpp" line="836"/>
         <source>The QMidiArp text file was imported. If you save this file, it will be saved using the newer xml format under the name
  '%1'.</source>
         <translation>Die QMidiArp Textdatei wurde importiert. Beim nächsten Speichern wird diese im neueren xml Format gespeichert unter dem Namen
  '%1'.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="836"/>
-        <location filename="../mainwindow.cpp" line="922"/>
+        <location filename="../mainwindow.cpp" line="862"/>
         <source>Could not write to file '%1'.</source>
         <translation>Konnte in die Datei '%1' nicht schreiben.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="999"/>
+        <location filename="../mainwindow.cpp" line="968"/>
         <source>Unnamed file was changed.
 Save changes?</source>
         <translation>Die unbenannte Datei wurde verändert.
 Die Änderungen speichern?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1001"/>
+        <location filename="../mainwindow.cpp" line="970"/>
         <source>File '%1' was changed.
 Save changes?</source>
         <translation>Die Datei '%1' wurde verändert.
 Die Änderungen speichern?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1005"/>
+        <location filename="../mainwindow.cpp" line="974"/>
         <source>Save changes</source>
         <translation>Änderungen speichern</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1135"/>
+        <location filename="../mainwindow.cpp" line="1104"/>
         <source>Could not read from resource file</source>
         <translation>Konnte Ressourcendatei nicht einlesen</translation>
     </message>
@@ -805,12 +832,12 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">&Start</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="145"/>
+        <location filename="../mainwindow.cpp" line="168"/>
         <source>&Use incoming MIDI Clock</source>
         <translation>&Eingehendes MIDI Clock Signal nutzen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="168"/>
+        <location filename="../mainwindow.cpp" line="191"/>
         <source>&Event Log</source>
         <translation>&Protokoll</translation>
     </message>
@@ -820,33 +847,33 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Strg+L</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="176"/>
+        <location filename="../mainwindow.cpp" line="199"/>
         <source>&Settings</source>
         <translation>&Einstellungen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="178"/>
+        <location filename="../mainwindow.cpp" line="201"/>
         <source>Ctrl+P</source>
         <comment>View|Settings</comment>
         <translation>Strg+P</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="215"/>
+        <location filename="../mainwindow.cpp" line="238"/>
         <source>&About Qt...</source>
         <translation>&Über Qt...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="284"/>
+        <location filename="../mainwindow.cpp" line="306"/>
         <source>About %1</source>
         <translation>Über %1</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="289"/>
+        <location filename="../mainwindow.cpp" line="311"/>
         <source>About Qt</source>
         <translation>Über Qt</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="298"/>
+        <location filename="../mainwindow.cpp" line="320"/>
         <source>Add MIDI Arpeggiator</source>
         <translation>MIDI-Arpeggiator hinzufügen</translation>
     </message>
@@ -859,59 +886,59 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">"%1" löschen?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="111"/>
-        <location filename="../mainwindow.cpp" line="546"/>
+        <location filename="../mainwindow.cpp" line="134"/>
+        <location filename="../mainwindow.cpp" line="572"/>
         <source>Open arpeggiator file</source>
         <translation>Arpeggiator-Datei öffnen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="140"/>
+        <location filename="../mainwindow.cpp" line="163"/>
         <source>Tempo of internal clock</source>
         <translation>Tempo der internen Uhr</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="583"/>
+        <location filename="../mainwindow.cpp" line="609"/>
         <source>Not a QMidiArp xml file.</source>
         <translation>Keine QMidiArp xml Datei.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="977"/>
+        <location filename="../mainwindow.cpp" line="946"/>
         <source>Save arpeggiator</source>
         <translation>Arpeggiator Speichern</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="977"/>
+        <location filename="../mainwindow.cpp" line="946"/>
         <source>QMidiArp files</source>
         <translation>QMidiArp Dateien</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1178"/>
+        <location filename="../mainwindow.cpp" line="1147"/>
         <source>Could not write to resource file</source>
         <translation>Konnte Ressourcendatei nicht speichern</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="88"/>
+        <location filename="../mainwindow.cpp" line="111"/>
         <source>&New Arp...</source>
         <translation>&Neuer Arp...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="90"/>
+        <location filename="../mainwindow.cpp" line="113"/>
         <source>Add new arpeggiator to tab bar</source>
         <translation>Neuen Arpeggiator hinzufügen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="95"/>
+        <location filename="../mainwindow.cpp" line="118"/>
         <source>Add new LFO to tab bar</source>
         <translation>Neuen LFO hinzufügen</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="89"/>
+        <location filename="../mainwindow.cpp" line="112"/>
         <source>Ctrl+A</source>
         <comment>Module|New Arp</comment>
         <translation>Strg+A</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="94"/>
+        <location filename="../mainwindow.cpp" line="117"/>
         <source>Ctrl+L</source>
         <comment>Module|New LFO</comment>
         <translation>Strg+L</translation>
@@ -927,12 +954,12 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Strg+Del</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="183"/>
+        <location filename="../mainwindow.cpp" line="206"/>
         <source>&File</source>
         <translation>&Datei</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="184"/>
+        <location filename="../mainwindow.cpp" line="207"/>
         <source>&View</source>
         <translation>&Ansicht</translation>
     </message>
@@ -941,12 +968,12 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">&Modul</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="186"/>
+        <location filename="../mainwindow.cpp" line="209"/>
         <source>&Help</source>
         <translation>&Hilfe</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="213"/>
+        <location filename="../mainwindow.cpp" line="236"/>
         <source>&About %1...</source>
         <translation>&Über %1...</translation>
     </message>
@@ -954,42 +981,42 @@ Die Änderungen speichern?</translation>
 <context>
     <name>MidiCCTable</name>
     <message>
-        <location filename="../midicctable.cpp" line="44"/>
+        <location filename="../midicctable.cpp" line="48"/>
         <source>Re&move</source>
         <translation>Ent&fernen</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="45"/>
+        <location filename="../midicctable.cpp" line="49"/>
         <source>Re&vert</source>
         <translation>&Wiederherstellen</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="58"/>
+        <location filename="../midicctable.cpp" line="62"/>
         <source>MIDI Controllers - </source>
         <translation>MIDI-Steuerungen - </translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="113"/>
+        <location filename="../midicctable.cpp" line="117"/>
         <source>Control</source>
         <translation>Steuerung</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="117"/>
+        <location filename="../midicctable.cpp" line="121"/>
         <source>CC#</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="121"/>
+        <location filename="../midicctable.cpp" line="125"/>
         <source>Ch</source>
         <translation>Kan</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="125"/>
+        <location filename="../midicctable.cpp" line="129"/>
         <source>min</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="129"/>
+        <location filename="../midicctable.cpp" line="133"/>
         <source>max</source>
         <translation></translation>
     </message>
@@ -997,22 +1024,22 @@ Die Änderungen speichern?</translation>
 <context>
     <name>PassWidget</name>
     <message>
-        <location filename="../passwidget.cpp" line="14"/>
+        <location filename="../passwidget.cpp" line="40"/>
         <source>&Forward unmatched events to port</source>
         <translation>&Unpassende MIDI Signale weiterleiten an Anschluss</translation>
     </message>
     <message>
-        <location filename="../passwidget.cpp" line="38"/>
+        <location filename="../passwidget.cpp" line="63"/>
         <source>&Compact module layout style</source>
         <translation>&Kompakter Layout-Stil für Module</translation>
     </message>
     <message>
-        <location filename="../passwidget.cpp" line="32"/>
+        <location filename="../passwidget.cpp" line="57"/>
         <source>&Modules controllable by MIDI controller</source>
         <translation>&Module sind MIDI-steuerbar</translation>
     </message>
     <message>
-        <location filename="../passwidget.cpp" line="57"/>
+        <location filename="../passwidget.cpp" line="82"/>
         <source>Settings - </source>
         <translation>Einstellungen - </translation>
     </message>
@@ -1032,92 +1059,134 @@ Die Änderungen speichern?</translation>
 <context>
     <name>SeqWidget</name>
     <message>
-        <location filename="../seqwidget.cpp" line="70"/>
+        <location filename="../seqwidget.cpp" line="92"/>
         <source>Transpose the sequence following incoming notes</source>
         <translation>Die Sequenz durch eingehende Note transponieren</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="105"/>
+        <location filename="../seqwidget.cpp" line="162"/>
         <source>Output</source>
         <translation>Ausgang</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="107"/>
+        <location filename="../seqwidget.cpp" line="164"/>
         <source>&Mute</source>
         <translation>&Stumm</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="116"/>
-        <location filename="../seqwidget.cpp" line="245"/>
-        <location filename="../seqwidget.cpp" line="262"/>
+        <location filename="../seqwidget.cpp" line="170"/>
+        <location filename="../seqwidget.cpp" line="253"/>
+        <location filename="../seqwidget.cpp" line="314"/>
+        <location filename="../seqwidget.cpp" line="333"/>
         <source>MIDI &Learn</source>
         <translation>&Lerne von MIDI</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="110"/>
+        <location filename="../seqwidget.cpp" line="59"/>
         <source>Cancel MIDI &Learning</source>
         <translation>MIDI-Lernen &Abbrechen</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="119"/>
-        <location filename="../seqwidget.cpp" line="248"/>
-        <location filename="../seqwidget.cpp" line="265"/>
+        <location filename="../seqwidget.cpp" line="94"/>
+        <source>&Note Off</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="98"/>
+        <source>Stop output when Note is released</source>
+        <translation>Keine Noten senden wenn die Taste losgelassen wird</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="106"/>
+        <source>&Restart</source>
+        <translation>&Neustarten</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="110"/>
+        <source>Restart sequence when a new note is received</source>
+        <translation>Die Sequenz neu starten wenn eine neue Note empfangen wird</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="112"/>
+        <source>&Trigger</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="116"/>
+        <source>Retrigger sequence when a new note is received</source>
+        <translation>Die Sequenz mit dem timing der empfangenen Noten starten (triggern)</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="118"/>
+        <source>&Loop</source>
+        <translation>&Schleife</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="122"/>
+        <source>Play sequence as loop instead of a single run</source>
+        <translation>Die Sequenz ständig wiederholen anstatt sie nur einmal zu spielen</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="175"/>
+        <location filename="../seqwidget.cpp" line="258"/>
+        <location filename="../seqwidget.cpp" line="319"/>
+        <location filename="../seqwidget.cpp" line="338"/>
         <source>MIDI &Forget</source>
         <translation>MIDI-Steuerungen &vergessen</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="167"/>
+        <location filename="../seqwidget.cpp" line="221"/>
         <source>Sequence</source>
         <translation>Sequenz</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="178"/>
+        <location filename="../seqwidget.cpp" line="232"/>
         <source>&Sequence</source>
         <translation>&Sequenz</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="184"/>
+        <location filename="../seqwidget.cpp" line="238"/>
         <source>Preset Number</source>
         <translation>Preset Index</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="189"/>
-        <location filename="../seqwidget.cpp" line="190"/>
+        <location filename="../seqwidget.cpp" line="243"/>
+        <location filename="../seqwidget.cpp" line="244"/>
         <source>Re&cord</source>
         <translation>&Aufnahme</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="191"/>
+        <location filename="../seqwidget.cpp" line="245"/>
         <source>Record step by step</source>
         <translation>Eingehende Noten Schritt für Schritt aufnehmen</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="208"/>
+        <location filename="../seqwidget.cpp" line="275"/>
         <source>Resolution (notes/beat): Number of notes produced every beat</source>
         <translation>Auflösung (Noten/Beat): Zahl der in jedem Vierteltakt gespielten Noten</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="220"/>
+        <location filename="../seqwidget.cpp" line="287"/>
         <source>Length of Sequence in beats</source>
         <translation>Länge der Sequenz in Vierteltakten</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="227"/>
+        <location filename="../seqwidget.cpp" line="294"/>
         <source>C&opy to new wave</source>
         <translation>In neue Wellenform &kopieren</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="241"/>
+        <location filename="../seqwidget.cpp" line="308"/>
         <source>Veloc&ity</source>
         <translation>An&schlag</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="881"/>
+        <location filename="../seqwidget.cpp" line="966"/>
         <source>Delete "%1"?</source>
         <translation>"%1" löschen?</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="900"/>
+        <location filename="../seqwidget.cpp" line="985"/>
         <source>New Name</source>
         <translation>Neuer Name</translation>
     </message>
@@ -1130,52 +1199,52 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">An&schlag</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="258"/>
+        <location filename="../seqwidget.cpp" line="328"/>
         <source>N&ote Length</source>
         <translation>N&otenlänge</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="72"/>
+        <location filename="../seqwidget.cpp" line="100"/>
         <source>&Velocity</source>
         <translation>&Anschlag</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="47"/>
+        <location filename="../seqwidget.cpp" line="69"/>
         <source>&Rename...</source>
         <translation>&Umbenennen...</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="48"/>
+        <location filename="../seqwidget.cpp" line="70"/>
         <source>Rename this Sequencer</source>
         <translation>Diesen Sequenzer umbenennen</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="53"/>
+        <location filename="../seqwidget.cpp" line="75"/>
         <source>&Delete...</source>
         <translation>&Löschen...</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="54"/>
+        <location filename="../seqwidget.cpp" line="76"/>
         <source>Delete this Sequencer</source>
         <translation>Diesen Sequenzer löschen</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="64"/>
+        <location filename="../seqwidget.cpp" line="86"/>
         <source>Input</source>
         <translation>Eingang</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="66"/>
+        <location filename="../seqwidget.cpp" line="88"/>
         <source>&Note</source>
         <translation>&Note</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="76"/>
+        <location filename="../seqwidget.cpp" line="104"/>
         <source>Set sequence velocity to that of incoming notes</source>
         <translation>Anschlag der Sequenz folgt dem der eingehende Noten </translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="81"/>
+        <location filename="../seqwidget.cpp" line="131"/>
         <source>&Channel</source>
         <translation>&Kanal</translation>
     </message>
@@ -1184,7 +1253,7 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">&Notenlänge</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="275"/>
+        <location filename="../seqwidget.cpp" line="346"/>
         <source>&Transpose</source>
         <translation>&Transponieren</translation>
     </message>
@@ -1197,12 +1266,12 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Nummer des gesendeten MIDI Controllers</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="128"/>
+        <location filename="../seqwidget.cpp" line="183"/>
         <source>&Port</source>
         <translation>Anschl&uß</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="135"/>
+        <location filename="../seqwidget.cpp" line="189"/>
         <source>C&hannel</source>
         <translation>Ka&nal</translation>
     </message>
@@ -1211,7 +1280,7 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Welle</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="171"/>
+        <location filename="../seqwidget.cpp" line="225"/>
         <source>Right button to mute points, left button to draw custom wave</source>
         <translation>Rechte Maustaste: Stummschalten einzelner Punkte, linke Maustaste: Zeichnen der Wellenform</translation>
     </message>
@@ -1232,7 +1301,7 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Frequenz (Zyklen pro beat): Anzahl der erzeugten Wellenzyklen pro Vierteltakt</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="198"/>
+        <location filename="../seqwidget.cpp" line="265"/>
         <source>&Resolution</source>
         <translation>&Auflösung</translation>
     </message>
@@ -1241,7 +1310,7 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Auflösung (Signale/beat): Anzahl der pro Vierteltakt erzeugten MIDI Signale</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="213"/>
+        <location filename="../seqwidget.cpp" line="280"/>
         <source>&Length</source>
         <translation>&Dauer</translation>
     </message>
@@ -1278,7 +1347,7 @@ Die Änderungen speichern?</translation>
         <translation type="obsolete">Rechteck</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="696"/>
+        <location filename="../seqwidget.cpp" line="757"/>
         <source>Custom</source>
         <translation>Frei</translation>
     </message>
diff --git a/src/translations/qmidiarp_es.ts b/src/translations/qmidiarp_es.ts
new file mode 100644
index 0000000..f0f6c46
--- /dev/null
+++ b/src/translations/qmidiarp_es.ts
@@ -0,0 +1,1148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="es">
+<context>
+    <name>ArpWidget</name>
+    <message>
+        <location filename="../arpwidget.cpp" line="73"/>
+        <source>&Rename...</source>
+        <translation>&Renombrar...</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="74"/>
+        <source>Rename this Arp</source>
+        <translation>Renombrar este arpegiador</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="79"/>
+        <source>&Delete...</source>
+        <translation>&Borrar...</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="80"/>
+        <source>Delete this Arp</source>
+        <translation>Borrar este arpegiador</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="90"/>
+        <source>Input</source>
+        <translation>Entrada</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="92"/>
+        <source>&Channel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="98"/>
+        <location filename="../arpwidget.cpp" line="454"/>
+        <source>Note Filter</source>
+        <translation>Filtro de nota</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="99"/>
+        <source>&Note</source>
+        <translation>&Nota</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="113"/>
+        <source>&Velocity</source>
+        <translation>&Velocidad</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="156"/>
+        <source>Output</source>
+        <translation>Salida</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="158"/>
+        <source>&Mute</source>
+        <translation>&Silenciar</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="164"/>
+        <location filename="../arpwidget.cpp" line="248"/>
+        <source>MIDI &Learn</source>
+        <translation>&Aprendizaje MIDI</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="63"/>
+        <source>Cancel MIDI &Learning</source>
+        <translation>&Cancelar aprendizaje MIDI</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="169"/>
+        <location filename="../arpwidget.cpp" line="253"/>
+        <source>MIDI &Forget</source>
+        <translation>&Olvidar MIDI</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="176"/>
+        <source>&Port</source>
+        <translation>&Puerto</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="182"/>
+        <source>C&hannel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="211"/>
+        <source>Pattern</source>
+        <translation>Patrón</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="216"/>
+        <source>&Edit Pattern</source>
+        <translation>&Editar patrón</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="224"/>
+        <source>&Remove Pattern</source>
+        <translation>&Borrar patrón</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="232"/>
+        <source>&Store Pattern</source>
+        <translation>&Guardar patrón</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="242"/>
+        <source>Pattern preset</source>
+        <translation>Patrón predefinido</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="262"/>
+        <source>Static</source>
+        <translation>Estático</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="262"/>
+        <source>Up</source>
+        <translation>Subiendo</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="262"/>
+        <source>Down</source>
+        <translation>Bajando</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="264"/>
+        <source>Repeat mode</source>
+        <translation>Modo repetición</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="271"/>
+        <source>No trigger</source>
+        <translation>Sin disparador</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="271"/>
+        <source>Kbd restart</source>
+        <translation>Reinicio de teclado</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="271"/>
+        <source>Kbd trigger</source>
+        <translation>Disparador de teclado</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="273"/>
+        <source>Trigger Mode</source>
+        <translation>Modo disparador</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="280"/>
+        <source>&Latch Mode</source>
+        <translation>&Modo bloqueo</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="313"/>
+        <source>0..9  note played on keyboard, 0 is lowest
+( ) numbers in parenthesis are stacked to chords
+  + = -  octave up/reset/down
+ < . > tempo up/reset/down
+  d h  note length up/down
+  / \  velocity up/down
+   p   pause</source>
+        <translation>0..9  nota recibida desde el teclado, 0 es la más grave
+( ) números entre paréntesis: agrupación para formar acordes
+  + = -  octava: superior/nominal/inferior
+ < . > tempo: aumentar/nominal/disminuir
+  d h  longitud de la nota: aumentar/disminuir
+  / \  velocidad: aumentar/disminuir
+   p   pausa</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="342"/>
+        <location filename="../arpwidget.cpp" line="860"/>
+        <source>Random</source>
+        <translation>Al azar</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="346"/>
+        <source>&Shift</source>
+        <translation>&Desfase</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="351"/>
+        <source>Vel&ocity</source>
+        <translation>&Velocidad</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="356"/>
+        <source>&Length</source>
+        <translation>&Longitud</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="375"/>
+        <location filename="../arpwidget.cpp" line="886"/>
+        <source>Envelope</source>
+        <translation>Envoltura</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="378"/>
+        <source>&Attack (s)</source>
+        <translation>&Ataque (s)</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="382"/>
+        <source>&Release (s)</source>
+        <translation>&Liberación (es)</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="450"/>
+        <source>Note Filter - ACTIVE</source>
+        <translation>Filtro de notas - ACTIVO</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="798"/>
+        <source>Could not read from resource file</source>
+        <translation>No se ha podido leer el archivo de recursos</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="856"/>
+        <source>Random - ACTIVE</source>
+        <translation>Aleatorización - ACTIVADA</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="882"/>
+        <source>Envelope - ACTIVE</source>
+        <translation>Envoltura - ACTIVADA</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="900"/>
+        <source>%1: Store pattern</source>
+        <translation>%1: Guardar patrón</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="901"/>
+        <source>New pattern</source>
+        <translation>Nuevo patrón</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="901"/>
+        <source>Arp pattern</source>
+        <translation>Patrón de Arp</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="937"/>
+        <source>Remove "%1"?</source>
+        <translation>¿Borrar "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="1021"/>
+        <source>Delete "%1"?</source>
+        <translation>¿Eliminar "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../arpwidget.cpp" line="1040"/>
+        <source>New Name</source>
+        <translation>Nuevo nombre</translation>
+    </message>
+</context>
+<context>
+    <name>GrooveWidget</name>
+    <message>
+        <location filename="../groovewidget.cpp" line="45"/>
+        <source>&Shift</source>
+        <translation>&Desfase</translation>
+    </message>
+    <message>
+        <location filename="../groovewidget.cpp" line="50"/>
+        <source>&Velocity</source>
+        <translation>&Velocidad</translation>
+    </message>
+    <message>
+        <location filename="../groovewidget.cpp" line="55"/>
+        <source>&Length</source>
+        <translation>&Longitud</translation>
+    </message>
+</context>
+<context>
+    <name>LfoWidget</name>
+    <message>
+        <location filename="../lfowidget.cpp" line="74"/>
+        <source>&Rename...</source>
+        <translation>&Renombrar...</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="75"/>
+        <source>Rename this LFO</source>
+        <translation>Renombrar este LFO</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="80"/>
+        <source>&Delete...</source>
+        <translation>&Borrar...</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="81"/>
+        <source>Delete this LFO</source>
+        <translation>Borrar este LFO</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="124"/>
+        <source>Output</source>
+        <translation>Salida</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="126"/>
+        <source>&Mute</source>
+        <translation>&Silenciar</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="64"/>
+        <source>Cancel MIDI &Learning</source>
+        <translation>&Cancelar aprendizaje MIDI</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="133"/>
+        <location filename="../lfowidget.cpp" line="225"/>
+        <location filename="../lfowidget.cpp" line="255"/>
+        <location filename="../lfowidget.cpp" line="303"/>
+        <location filename="../lfowidget.cpp" line="322"/>
+        <location filename="../lfowidget.cpp" line="341"/>
+        <source>MIDI &Learn</source>
+        <translation>&Aprendizaje MIDI</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="138"/>
+        <location filename="../lfowidget.cpp" line="230"/>
+        <location filename="../lfowidget.cpp" line="260"/>
+        <location filename="../lfowidget.cpp" line="308"/>
+        <location filename="../lfowidget.cpp" line="327"/>
+        <location filename="../lfowidget.cpp" line="346"/>
+        <source>MIDI &Forget</source>
+        <translation>&Olvidar MIDI</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="93"/>
+        <location filename="../lfowidget.cpp" line="146"/>
+        <source>MIDI &CC#</source>
+        <translation>MIDI &CC#</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="91"/>
+        <source>Input</source>
+        <translation>Entrada</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="99"/>
+        <source>MIDI Controller number to record</source>
+        <translation>Número de controlador MIDI a grabar</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="104"/>
+        <source>&Channel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="152"/>
+        <source>MIDI Controller number sent to output</source>
+        <translation>Número de controlador MIDI enviado a la salida</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="156"/>
+        <source>&Port</source>
+        <translation>&Puerto</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="162"/>
+        <source>C&hannel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="196"/>
+        <source>Wave</source>
+        <translation>Onda</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="200"/>
+        <source>Right button to mute points
+Left button to draw custom wave
+Wheel to change offset</source>
+        <translation>Botón derecho para silenciar puntos
+Botón izquierdo para dibujar onda personalizada
+Rueda para cambiar desplazamiento</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="208"/>
+        <source>&Waveform</source>
+        <translation>&Forma de onda</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="219"/>
+        <source>Waveform Basis</source>
+        <translation>Forma de onda base</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="238"/>
+        <source>&Frequency</source>
+        <translation>&Frecuencia</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="248"/>
+        <source>Frequency (cycles/beat): Number of wave cycles produced every beat</source>
+        <translation>Frecuencia (ciclos/tiempo): Numero de ciclos de onda producidos en cada tiempo</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="267"/>
+        <source>&Resolution</source>
+        <translation>&Resolución</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="276"/>
+        <source>Resolution (events/beat): Number of events produced every beat</source>
+        <translation>Resolución (eventos/tiempo): Número de eventos producidos por cada tiempo</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="281"/>
+        <source>&Length</source>
+        <translation>&Longitud</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="288"/>
+        <source>Length of LFO wave in beats</source>
+        <translation>Longitud de la onda LFO en tiempos</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="293"/>
+        <location filename="../lfowidget.cpp" line="294"/>
+        <source>Re&cord</source>
+        <translation>&Grabar</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="295"/>
+        <source>Record incoming controller</source>
+        <translation>Grabar controlador entrante</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="316"/>
+        <source>&Amplitude</source>
+        <translation>&Amplitud</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="336"/>
+        <source>&Offset</source>
+        <translation>&Desplazamiento</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="746"/>
+        <source>Sine</source>
+        <translation>Seno</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="746"/>
+        <source>Saw up</source>
+        <translation>Sierra hacia arriba</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="746"/>
+        <source>Triangle</source>
+        <translation>Triángulo</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="747"/>
+        <source>Saw down</source>
+        <translation>Sierra hacia abajo</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="747"/>
+        <source>Square</source>
+        <translation>Cuadrado</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="747"/>
+        <source>Custom</source>
+        <translation>Personalizado</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="948"/>
+        <source>Delete "%1"?</source>
+        <translation>¿Eliminar "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="967"/>
+        <source>New Name</source>
+        <translation>Nuevo nombre</translation>
+    </message>
+</context>
+<context>
+    <name>LogWidget</name>
+    <message>
+        <location filename="../logwidget.cpp" line="49"/>
+        <source>&Enable Log</source>
+        <translation>&Habilitar registro</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="55"/>
+        <source>Log &MIDI Clock</source>
+        <translation>Registrar reloj &MIDI</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="60"/>
+        <source>&Clear</source>
+        <translation>&Limpiar</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="113"/>
+        <source>MIDI Clock</source>
+        <translation>Reloj MIDI</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="118"/>
+        <source>MIDI Start (Transport)</source>
+        <translation>MIDI Iniciar (transporte)</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="122"/>
+        <source>MIDI Continue (Transport)</source>
+        <translation>MIDI Continuar (transporte)</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="126"/>
+        <source>MIDI Stop (Transport)</source>
+        <translation>MIDI Parar (transporte)</translation>
+    </message>
+    <message>
+        <location filename="../logwidget.cpp" line="130"/>
+        <source>Unknown event type</source>
+        <translation>Tipo de evento desconocido</translation>
+    </message>
+</context>
+<context>
+    <name>MainWindow</name>
+    <message>
+        <location filename="../mainwindow.cpp" line="81"/>
+        <source>Event Log</source>
+        <translation>Registro de eventos</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="96"/>
+        <source>Groove</source>
+        <translation>Groove</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="111"/>
+        <source>&New Arp...</source>
+        <translation>&Nuevo Arp...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="112"/>
+        <source>Ctrl+A</source>
+        <comment>Module|New Arp</comment>
+        <translation>Ctrl+A</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="113"/>
+        <source>Add new arpeggiator to tab bar</source>
+        <translation>Añadir nuevo arpegiador a la barra de pestañas</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="116"/>
+        <source>&New LFO...</source>
+        <translation>&Nuevo LFO...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="117"/>
+        <source>Ctrl+L</source>
+        <comment>Module|New LFO</comment>
+        <translation>Ctrl+L</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="118"/>
+        <source>Add new LFO to tab bar</source>
+        <translation>Añadir nuevo LFO a la barra de pestañas</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="121"/>
+        <source>&New Sequencer...</source>
+        <translation>&Nuevo secuenciador...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="122"/>
+        <source>Ctrl+T</source>
+        <comment>Module|New Sequencer</comment>
+        <translation>Ctrl+T</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="123"/>
+        <source>Add new Sequencer to tab bar</source>
+        <translation>Añadir nuevo secuenciador a la barra de pestañas</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="127"/>
+        <source>&New</source>
+        <translation>&Nuevo</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="129"/>
+        <source>Create new arpeggiator file</source>
+        <translation>Crear nuevo archivo de arpegiador</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="132"/>
+        <source>&Open...</source>
+        <translation>&Abrir...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="134"/>
+        <location filename="../mainwindow.cpp" line="572"/>
+        <source>Open arpeggiator file</source>
+        <translation>Abrir archivo de arpegiador</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="137"/>
+        <source>&Save</source>
+        <translation>&Guardar</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="139"/>
+        <source>Save current arpeggiator file</source>
+        <translation>Guardar archivo de arpegiador actual</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="142"/>
+        <source>Save &as...</source>
+        <translation>Guardar &como...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="145"/>
+        <source>Save current arpeggiator file with new name</source>
+        <translation>Guardar archivo de arpegiador actual con un nuevo nombre</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="148"/>
+        <source>&Quit</source>
+        <translation>&Terminar</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="149"/>
+        <source>Ctrl+Q</source>
+        <comment>File|Quit</comment>
+        <translation>Ctrl+Q</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="150"/>
+        <source>Quit application</source>
+        <translation>Terminar la aplicación</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="153"/>
+        <source>&Run with internal clock</source>
+        <translation>&Ejecutar con reloj interno</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="163"/>
+        <source>Tempo of internal clock</source>
+        <translation>Tempo del reloj interno</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="168"/>
+        <source>&Use incoming MIDI Clock</source>
+        <translation>&Utlizar recepción de reloj MIDI</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="177"/>
+        <source>&Connect to Jack Transport</source>
+        <translation>&Conectar con el transporte de Jack</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="191"/>
+        <source>&Event Log</source>
+        <translation>&Registro de eventos</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="192"/>
+        <source>Ctrl+H</source>
+        <comment>View|Event Log</comment>
+        <translation>Ctrl+H</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="196"/>
+        <source>&Groove Settings</source>
+        <translation>Preferencias de &Groove</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="197"/>
+        <source>Ctrl+G</source>
+        <comment>View|Groove</comment>
+        <translation>Ctrl+G</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="199"/>
+        <source>&Settings</source>
+        <translation>&Preferencias</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="201"/>
+        <source>Ctrl+P</source>
+        <comment>View|Settings</comment>
+        <translation>Ctrl+P</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="206"/>
+        <source>&File</source>
+        <translation>&Archivo</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="207"/>
+        <source>&View</source>
+        <translation>&Vista</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="208"/>
+        <source>Mod&ule</source>
+        <translation>&Módulo</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="209"/>
+        <source>&Help</source>
+        <translation>A&yuda</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="214"/>
+        <source>&Recently opened files</source>
+        <translation>Archivos abiertos &recientemente</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="228"/>
+        <source>&MIDI Controllers...</source>
+        <translation>Controladores &MIDI...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="236"/>
+        <source>&About %1...</source>
+        <translation>&Acerca de %1...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="238"/>
+        <source>&About Qt...</source>
+        <translation>Acerca de &Qt...</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="241"/>
+        <source>&File Toolbar</source>
+        <translation>Barra de herramientas de &archivo</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="249"/>
+        <source>&Control Toolbar</source>
+        <translation>Barra de herramientas de &Control</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="306"/>
+        <source>About %1</source>
+        <translation>Acerca de %1</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="311"/>
+        <source>About Qt</source>
+        <translation>Acerca de Qt</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="320"/>
+        <source>Add MIDI Arpeggiator</source>
+        <translation>Añadir arpegiador MIDI</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="321"/>
+        <location filename="../mainwindow.cpp" line="334"/>
+        <location filename="../mainwindow.cpp" line="347"/>
+        <source>%1</source>
+        <translation>%1</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="333"/>
+        <source>Add MIDI LFO</source>
+        <translation>Añadir LFO MIDI</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="346"/>
+        <source>Add Step Sequencer</source>
+        <translation>Añadir secuenciador de pasos</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="573"/>
+        <source>QMidiArp XML files</source>
+        <translation>Archivos XML de QMidiArp</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="574"/>
+        <source>Old QMidiArp files</source>
+        <translation>Archivos antiguos de QMidiArp</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="593"/>
+        <location filename="../mainwindow.cpp" line="749"/>
+        <source>Could not read from file '%1'.</source>
+        <translation>No se ha podido leer el archivo '%1'.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="609"/>
+        <source>Not a QMidiArp xml file.</source>
+        <translation>No es un archivo XML de QMidiArp.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="611"/>
+        <source>This is not a valid xml file for </source>
+        <translation>Este no es un archivo XML válido para</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="836"/>
+        <source>The QMidiArp text file was imported. If you save this file, it will be saved using the newer xml format under the name
+ '%1'.</source>
+        <translation>El archivo de texto de QMidiArp ha sido importado. Si se guarda este archivo, será almacenado utilizando el nuevo formato XML bajo el nombre
+ '%1'.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="862"/>
+        <source>Could not write to file '%1'.</source>
+        <translation>No se ha podido grabar el archivo '%1'.</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="946"/>
+        <source>Save arpeggiator</source>
+        <translation>Guardar arpegiador</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="946"/>
+        <source>QMidiArp files</source>
+        <translation>Archivos de QMidiArp</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="968"/>
+        <source>Unnamed file was changed.
+Save changes?</source>
+        <translation>El archivo sin nombre ha cambiado. ¿Guardarlo?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="970"/>
+        <source>File '%1' was changed.
+Save changes?</source>
+        <translation>Archivo '%1' ha cambiado. ¿Guardarlo?</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="974"/>
+        <source>Save changes</source>
+        <translation>Guardar cambios</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1104"/>
+        <source>Could not read from resource file</source>
+        <translation>No se ha podido leer el archivo de recursos</translation>
+    </message>
+    <message>
+        <location filename="../mainwindow.cpp" line="1147"/>
+        <source>Could not write to resource file</source>
+        <translation>No se ha podido guardar el archivo de recursos</translation>
+    </message>
+</context>
+<context>
+    <name>MidiCCTable</name>
+    <message>
+        <location filename="../midicctable.cpp" line="48"/>
+        <source>Re&move</source>
+        <translation>&Borrar</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="49"/>
+        <source>Re&vert</source>
+        <translation>Re&vertir</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="62"/>
+        <source>MIDI Controllers - </source>
+        <translation>Controladores MIDI</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="117"/>
+        <source>Control</source>
+        <translation>Control</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="121"/>
+        <source>CC#</source>
+        <translation>CC#</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="125"/>
+        <source>Ch</source>
+        <translation>Canal</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="129"/>
+        <source>min</source>
+        <translation>min</translation>
+    </message>
+    <message>
+        <location filename="../midicctable.cpp" line="133"/>
+        <source>max</source>
+        <translation>max</translation>
+    </message>
+</context>
+<context>
+    <name>PassWidget</name>
+    <message>
+        <location filename="../passwidget.cpp" line="40"/>
+        <source>&Forward unmatched events to port</source>
+        <translation>&Redireccionar eventos no coincidentes al puerto</translation>
+    </message>
+    <message>
+        <location filename="../passwidget.cpp" line="57"/>
+        <source>&Modules controllable by MIDI controller</source>
+        <translation>&Módulos controlables por controlador MIDI</translation>
+    </message>
+    <message>
+        <location filename="../passwidget.cpp" line="63"/>
+        <source>&Compact module layout style</source>
+        <translation>Estilo &compacto de distribución de módulo</translation>
+    </message>
+    <message>
+        <location filename="../passwidget.cpp" line="82"/>
+        <source>Settings - </source>
+        <translation>Preferencias - </translation>
+    </message>
+</context>
+<context>
+    <name>SeqWidget</name>
+    <message>
+        <location filename="../seqwidget.cpp" line="69"/>
+        <source>&Rename...</source>
+        <translation>&Renombrar...</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="70"/>
+        <source>Rename this Sequencer</source>
+        <translation>Renombrar este secuenciador</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="75"/>
+        <source>&Delete...</source>
+        <translation>&Borrar...</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="76"/>
+        <source>Delete this Sequencer</source>
+        <translation>Borrar este secuenciador</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="86"/>
+        <source>Input</source>
+        <translation>Entrada</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="88"/>
+        <source>&Note</source>
+        <translation>&Nota</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="92"/>
+        <source>Transpose the sequence following incoming notes</source>
+        <translation>Transportar la secuencia siguiendo las notas siguientes</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="100"/>
+        <source>&Velocity</source>
+        <translation>&Velocidad</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="104"/>
+        <source>Set sequence velocity to that of incoming notes</source>
+        <translation>Establecer la velocidad de la secuencia igual a las notas recibidas</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="131"/>
+        <source>&Channel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="162"/>
+        <source>Output</source>
+        <translation>Salida</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="164"/>
+        <source>&Mute</source>
+        <translation>&Silenciar</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="59"/>
+        <source>Cancel MIDI &Learning</source>
+        <translation>&Cancelar aprendizaje MIDI</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="94"/>
+        <source>&Note Off</source>
+        <translation>Desconectar &Nota</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="98"/>
+        <source>Stop output when Note is released</source>
+        <translation>Parar la salida cuando la nota sea liberada</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="106"/>
+        <source>&Restart</source>
+        <translation>&Reinicio</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="110"/>
+        <source>Restart sequence when a new note is received</source>
+        <translation>Reiniciar la secuencia cuando una nueva nota sea recibida</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="112"/>
+        <source>&Trigger</source>
+        <translation>&Disparador</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="116"/>
+        <source>Retrigger sequence when a new note is received</source>
+        <translation>Volver a disparar la secuencia cuando una nueva nota sea recibida</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="118"/>
+        <source>&Loop</source>
+        <translation>&Bucle</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="122"/>
+        <source>Play sequence as loop instead of a single run</source>
+        <translation>Reproducir la secuencia en bucle en lugar de una vez</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="170"/>
+        <location filename="../seqwidget.cpp" line="253"/>
+        <location filename="../seqwidget.cpp" line="314"/>
+        <location filename="../seqwidget.cpp" line="333"/>
+        <source>MIDI &Learn</source>
+        <translation>&Aprendizaje MIDI</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="175"/>
+        <location filename="../seqwidget.cpp" line="258"/>
+        <location filename="../seqwidget.cpp" line="319"/>
+        <location filename="../seqwidget.cpp" line="338"/>
+        <source>MIDI &Forget</source>
+        <translation>&Olvidar MIDI</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="183"/>
+        <source>&Port</source>
+        <translation>&Puerto</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="189"/>
+        <source>C&hannel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="221"/>
+        <source>Sequence</source>
+        <translation>Secuencia</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="225"/>
+        <source>Right button to mute points, left button to draw custom wave</source>
+        <translation>Botón derecho para silenciar puntos, botón izquierdo para dibujar onda personalizada</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="232"/>
+        <source>&Sequence</source>
+        <translation>&Secuencia</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="238"/>
+        <source>Preset Number</source>
+        <translation>Número de patrón</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="243"/>
+        <location filename="../seqwidget.cpp" line="244"/>
+        <source>Re&cord</source>
+        <translation>&Grabar</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="245"/>
+        <source>Record step by step</source>
+        <translation>Grabar paso a paso</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="265"/>
+        <source>&Resolution</source>
+        <translation>&Resolución</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="275"/>
+        <source>Resolution (notes/beat): Number of notes produced every beat</source>
+        <translation>Resolución (notas/tiempo): número de notas producidas por cada tiempo</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="280"/>
+        <source>&Length</source>
+        <translation>&Longitud</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="287"/>
+        <source>Length of Sequence in beats</source>
+        <translation>Longitud de la secuencia en tiempos</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="294"/>
+        <source>C&opy to new wave</source>
+        <translation>C&opiar a nueva onda</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="308"/>
+        <source>Veloc&ity</source>
+        <translation>&Velocidad</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="328"/>
+        <source>N&ote Length</source>
+        <translation>&Longitud de nota</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="346"/>
+        <source>&Transpose</source>
+        <translation>&Transportar</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="757"/>
+        <source>Custom</source>
+        <translation>Personalizado</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="966"/>
+        <source>Delete "%1"?</source>
+        <translation>¿Eliminar "%1"?</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="985"/>
+        <source>New Name</source>
+        <translation>Nuevo nombre</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/qmidiarp_fr.ts b/src/translations/qmidiarp_fr.ts
index 66fa22d..17fe545 100644
--- a/src/translations/qmidiarp_fr.ts
+++ b/src/translations/qmidiarp_fr.ts
@@ -4,47 +4,47 @@
 <context>
     <name>ArpWidget</name>
     <message>
-        <location filename="../arpwidget.cpp" line="49"/>
+        <location filename="../arpwidget.cpp" line="90"/>
         <source>Input</source>
         <translation>Entrée</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="59"/>
+        <location filename="../arpwidget.cpp" line="99"/>
         <source>&Note</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="116"/>
+        <location filename="../arpwidget.cpp" line="156"/>
         <source>Output</source>
         <translation>Sortie</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="136"/>
+        <location filename="../arpwidget.cpp" line="176"/>
         <source>&Port</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="173"/>
+        <location filename="../arpwidget.cpp" line="211"/>
         <source>Pattern</source>
         <translation>Motif</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="178"/>
+        <location filename="../arpwidget.cpp" line="216"/>
         <source>&Edit Pattern</source>
         <translation>&Editer le Motif</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="186"/>
+        <location filename="../arpwidget.cpp" line="224"/>
         <source>&Remove Pattern</source>
         <translation>&Supprimer le Motif</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="194"/>
+        <location filename="../arpwidget.cpp" line="232"/>
         <source>&Store Pattern</source>
         <translation>&Mémoriser le Motif</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="204"/>
+        <location filename="../arpwidget.cpp" line="242"/>
         <source>Pattern preset</source>
         <translation>Preset de motif</translation>
     </message>
@@ -69,49 +69,49 @@ d h durée de note augm/dim
  p pause</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="339"/>
+        <location filename="../arpwidget.cpp" line="378"/>
         <source>&Attack (s)</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="343"/>
+        <location filename="../arpwidget.cpp" line="382"/>
         <source>&Release (s)</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="303"/>
-        <location filename="../arpwidget.cpp" line="865"/>
+        <location filename="../arpwidget.cpp" line="342"/>
+        <location filename="../arpwidget.cpp" line="860"/>
         <source>Random</source>
         <translation>Randomisation</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="225"/>
+        <location filename="../arpwidget.cpp" line="264"/>
         <source>Repeat mode</source>
         <translation>Mode de répétition</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="232"/>
+        <location filename="../arpwidget.cpp" line="271"/>
         <source>Kbd trigger</source>
         <translation>Déclenche</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="307"/>
+        <location filename="../arpwidget.cpp" line="346"/>
         <source>&Shift</source>
         <translation>&Décalage</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="312"/>
+        <location filename="../arpwidget.cpp" line="351"/>
         <source>Vel&ocity</source>
         <translation>Vél&ocité</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="317"/>
+        <location filename="../arpwidget.cpp" line="356"/>
         <source>&Length</source>
         <translation>&Durée</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="336"/>
-        <location filename="../arpwidget.cpp" line="891"/>
+        <location filename="../arpwidget.cpp" line="375"/>
+        <location filename="../arpwidget.cpp" line="886"/>
         <source>Envelope</source>
         <translation>Enveloppe</translation>
     </message>
@@ -120,141 +120,140 @@ d h durée de note augm/dim
         <translation type="obsolete">Erreur d'écriture du fichier ressources</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="803"/>
+        <location filename="../arpwidget.cpp" line="798"/>
         <source>Could not read from resource file</source>
         <translation>Erreur de lecture du fichier ressources</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="861"/>
+        <location filename="../arpwidget.cpp" line="856"/>
         <source>Random - ACTIVE</source>
         <translation>Randomisation - ACTIVE</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="887"/>
+        <location filename="../arpwidget.cpp" line="882"/>
         <source>Envelope - ACTIVE</source>
         <translation>Enveloppe - ACTIVE</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="905"/>
+        <location filename="../arpwidget.cpp" line="900"/>
         <source>%1: Store pattern</source>
         <translation>%1: Mémoriser le motif</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="906"/>
+        <location filename="../arpwidget.cpp" line="901"/>
         <source>New pattern</source>
         <translation>Nouveau motif</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="906"/>
+        <location filename="../arpwidget.cpp" line="901"/>
         <source>Arp pattern</source>
         <translation>Motif d'arpège</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="942"/>
+        <location filename="../arpwidget.cpp" line="937"/>
         <source>Remove "%1"?</source>
         <translation>Supprimer "%1"?</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="51"/>
+        <location filename="../arpwidget.cpp" line="92"/>
         <source>&Channel</source>
         <translation>&Canal</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="32"/>
+        <location filename="../arpwidget.cpp" line="73"/>
         <source>&Rename...</source>
         <translation>&Renommer...</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="33"/>
+        <location filename="../arpwidget.cpp" line="74"/>
         <source>Rename this Arp</source>
         <translation>Renommer cet arpégiateur</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="38"/>
+        <location filename="../arpwidget.cpp" line="79"/>
         <source>&Delete...</source>
         <translation>&Supprimer...</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="39"/>
+        <location filename="../arpwidget.cpp" line="80"/>
         <source>Delete this Arp</source>
         <translation>Supprimer cet arpégiateur</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="58"/>
-        <location filename="../arpwidget.cpp" line="414"/>
+        <location filename="../arpwidget.cpp" line="98"/>
+        <location filename="../arpwidget.cpp" line="454"/>
         <source>Note Filter</source>
         <translation>Filtre</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="73"/>
+        <location filename="../arpwidget.cpp" line="113"/>
         <source>&Velocity</source>
         <translation>&Vélocité</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="118"/>
+        <location filename="../arpwidget.cpp" line="158"/>
         <source>&Mute</source>
         <translation>&Muet</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="122"/>
-        <location filename="../arpwidget.cpp" line="210"/>
+        <location filename="../arpwidget.cpp" line="164"/>
+        <location filename="../arpwidget.cpp" line="248"/>
         <source>MIDI &Learn</source>
         <translation>&Apprendre du MIDI</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="125"/>
-        <location filename="../arpwidget.cpp" line="213"/>
+        <location filename="../arpwidget.cpp" line="63"/>
         <source>Cancel MIDI &Learning</source>
         <translation>&Annuler l'apprentissage</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="129"/>
-        <location filename="../arpwidget.cpp" line="217"/>
+        <location filename="../arpwidget.cpp" line="169"/>
+        <location filename="../arpwidget.cpp" line="253"/>
         <source>MIDI &Forget</source>
         <translation>&Oublier les contrôleurs MIDI</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="143"/>
+        <location filename="../arpwidget.cpp" line="182"/>
         <source>C&hannel</source>
         <translation>C&anal</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="223"/>
+        <location filename="../arpwidget.cpp" line="262"/>
         <source>Static</source>
         <translation>Statique</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="223"/>
+        <location filename="../arpwidget.cpp" line="262"/>
         <source>Up</source>
         <translation>Monte</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="223"/>
+        <location filename="../arpwidget.cpp" line="262"/>
         <source>Down</source>
         <translation>Descend</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="232"/>
+        <location filename="../arpwidget.cpp" line="271"/>
         <source>No trigger</source>
         <translation>Continu</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="232"/>
+        <location filename="../arpwidget.cpp" line="271"/>
         <source>Kbd restart</source>
         <translation>Redémarre</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="234"/>
+        <location filename="../arpwidget.cpp" line="273"/>
         <source>Trigger Mode</source>
         <translation>Mode de déclenchement par clavier</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="241"/>
+        <location filename="../arpwidget.cpp" line="280"/>
         <source>&Latch Mode</source>
         <translation>Véroui&llage clavier</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="274"/>
+        <location filename="../arpwidget.cpp" line="313"/>
         <source>0..9  note played on keyboard, 0 is lowest
 ( ) numbers in parenthesis are stacked to chords
   + = -  octave up/reset/down
@@ -271,17 +270,17 @@ d h durée de note augm/dim
  p pause</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="410"/>
+        <location filename="../arpwidget.cpp" line="450"/>
         <source>Note Filter - ACTIVE</source>
         <translation>Filtre - AKTIF</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="990"/>
+        <location filename="../arpwidget.cpp" line="1021"/>
         <source>Delete "%1"?</source>
         <translation>Supprimer "%1"?</translation>
     </message>
     <message>
-        <location filename="../arpwidget.cpp" line="1009"/>
+        <location filename="../arpwidget.cpp" line="1040"/>
         <source>New Name</source>
         <translation>Nouveau nom</translation>
     </message>
@@ -289,17 +288,17 @@ d h durée de note augm/dim
 <context>
     <name>GrooveWidget</name>
     <message>
-        <location filename="../groovewidget.cpp" line="20"/>
+        <location filename="../groovewidget.cpp" line="45"/>
         <source>&Shift</source>
         <translation>&Décalage</translation>
     </message>
     <message>
-        <location filename="../groovewidget.cpp" line="25"/>
+        <location filename="../groovewidget.cpp" line="50"/>
         <source>&Velocity</source>
         <translation>&Vélocité</translation>
     </message>
     <message>
-        <location filename="../groovewidget.cpp" line="30"/>
+        <location filename="../groovewidget.cpp" line="55"/>
         <source>&Length</source>
         <translation>&Durée</translation>
     </message>
@@ -307,80 +306,98 @@ d h durée de note augm/dim
 <context>
     <name>LfoWidget</name>
     <message>
-        <location filename="../lfowidget.cpp" line="68"/>
+        <location filename="../lfowidget.cpp" line="124"/>
         <source>Output</source>
         <translation>Sortie</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="70"/>
+        <location filename="../lfowidget.cpp" line="126"/>
         <source>&Mute</source>
         <translation>&Muet</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="92"/>
+        <location filename="../lfowidget.cpp" line="93"/>
+        <location filename="../lfowidget.cpp" line="146"/>
         <source>MIDI &CC#</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="98"/>
+        <location filename="../lfowidget.cpp" line="152"/>
         <source>MIDI Controller number sent to output</source>
         <translation>Numéro du contrôleur envoyé à la sortie</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="109"/>
+        <location filename="../lfowidget.cpp" line="162"/>
         <source>C&hannel</source>
         <translation>C&anal</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="102"/>
+        <location filename="../lfowidget.cpp" line="156"/>
         <source>&Port</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="51"/>
+        <location filename="../lfowidget.cpp" line="74"/>
         <source>&Rename...</source>
         <translation>&Renommer...</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="52"/>
+        <location filename="../lfowidget.cpp" line="75"/>
         <source>Rename this LFO</source>
         <translation>Renommer ce LFO</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="57"/>
+        <location filename="../lfowidget.cpp" line="80"/>
         <source>&Delete...</source>
         <translation>&Supprimer...</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="58"/>
+        <location filename="../lfowidget.cpp" line="81"/>
         <source>Delete this LFO</source>
         <translation>Supprimer ce LFO</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="79"/>
-        <location filename="../lfowidget.cpp" line="166"/>
-        <location filename="../lfowidget.cpp" line="192"/>
-        <location filename="../lfowidget.cpp" line="232"/>
-        <location filename="../lfowidget.cpp" line="249"/>
+        <location filename="../lfowidget.cpp" line="91"/>
+        <source>Input</source>
+        <translation>Entrée</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="99"/>
+        <source>MIDI Controller number to record</source>
+        <translation>Le numéro du contrôleur MIDI à enregistrer</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="104"/>
+        <source>&Channel</source>
+        <translation>&Canal</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="133"/>
+        <location filename="../lfowidget.cpp" line="225"/>
+        <location filename="../lfowidget.cpp" line="255"/>
+        <location filename="../lfowidget.cpp" line="303"/>
+        <location filename="../lfowidget.cpp" line="322"/>
+        <location filename="../lfowidget.cpp" line="341"/>
         <source>MIDI &Learn</source>
         <translation>&Apprendre du MIDI</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="73"/>
+        <location filename="../lfowidget.cpp" line="64"/>
         <source>Cancel MIDI &Learning</source>
         <translation>&Annuler l'apprentissage</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="82"/>
-        <location filename="../lfowidget.cpp" line="169"/>
-        <location filename="../lfowidget.cpp" line="195"/>
-        <location filename="../lfowidget.cpp" line="235"/>
-        <location filename="../lfowidget.cpp" line="252"/>
+        <location filename="../lfowidget.cpp" line="138"/>
+        <location filename="../lfowidget.cpp" line="230"/>
+        <location filename="../lfowidget.cpp" line="260"/>
+        <location filename="../lfowidget.cpp" line="308"/>
+        <location filename="../lfowidget.cpp" line="327"/>
+        <location filename="../lfowidget.cpp" line="346"/>
         <source>MIDI &Forget</source>
         <translation>&Oublier les contrôleurs MIDI</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="137"/>
+        <location filename="../lfowidget.cpp" line="196"/>
         <source>Wave</source>
         <translation>Forme</translation>
     </message>
@@ -389,12 +406,12 @@ d h durée de note augm/dim
         <translation type="obsolete">Bouton droit de la souris pour rendre muet chaque point, bouton gauche pour dessiner la forme d'onde</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="149"/>
+        <location filename="../lfowidget.cpp" line="208"/>
         <source>&Waveform</source>
         <translation>Forme d'&onde</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="160"/>
+        <location filename="../lfowidget.cpp" line="219"/>
         <source>Waveform Basis</source>
         <translation>Base de la forme d'onde</translation>
     </message>
@@ -419,7 +436,7 @@ d h durée de note augm/dim
         <translation type="obsolete">&Longueur (beats)</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="141"/>
+        <location filename="../lfowidget.cpp" line="200"/>
         <source>Right button to mute points
 Left button to draw custom wave
 Wheel to change offset</source>
@@ -428,32 +445,32 @@ Bouton gauche pour dessiner la forme d'onde
 Molette pour changer l'offset</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="175"/>
+        <location filename="../lfowidget.cpp" line="238"/>
         <source>&Frequency</source>
         <translation>&Fréquence</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="185"/>
+        <location filename="../lfowidget.cpp" line="248"/>
         <source>Frequency (cycles/beat): Number of wave cycles produced every beat</source>
         <translation>Fréquence (cycles/beat): Nombre de cycles d'onde produits à chaque quart de temps</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="201"/>
+        <location filename="../lfowidget.cpp" line="267"/>
         <source>&Resolution</source>
         <translation>&Résolution</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="210"/>
+        <location filename="../lfowidget.cpp" line="276"/>
         <source>Resolution (events/beat): Number of events produced every beat</source>
         <translation>Résolution (év/beat): Nombre d'évênements produits à chaque quart de temps</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="215"/>
+        <location filename="../lfowidget.cpp" line="281"/>
         <source>&Length</source>
         <translation>&Durée</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="222"/>
+        <location filename="../lfowidget.cpp" line="288"/>
         <source>Length of LFO wave in beats</source>
         <translation>Longueur de la forme d'onde du LFO en quarts de temps (beats)</translation>
     </message>
@@ -462,52 +479,63 @@ Molette pour changer l'offset</translation>
         <translation type="obsolete">&Copier dans la forme d'onde libre</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="228"/>
+        <location filename="../lfowidget.cpp" line="293"/>
+        <location filename="../lfowidget.cpp" line="294"/>
+        <source>Re&cord</source>
+        <translation>&Enregistrer</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="295"/>
+        <source>Record incoming controller</source>
+        <translation>Enregistrer les évênements contrôleurs reçus à l'entrée</translation>
+    </message>
+    <message>
+        <location filename="../lfowidget.cpp" line="316"/>
         <source>&Amplitude</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="245"/>
+        <location filename="../lfowidget.cpp" line="336"/>
         <source>&Offset</source>
         <translation>&Décalage</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="668"/>
+        <location filename="../lfowidget.cpp" line="746"/>
         <source>Sine</source>
         <translation>Sinus</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="668"/>
+        <location filename="../lfowidget.cpp" line="746"/>
         <source>Saw up</source>
         <translation>Scie montant</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="668"/>
+        <location filename="../lfowidget.cpp" line="746"/>
         <source>Triangle</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="669"/>
+        <location filename="../lfowidget.cpp" line="747"/>
         <source>Saw down</source>
         <translation>Scie descendant</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="669"/>
+        <location filename="../lfowidget.cpp" line="747"/>
         <source>Square</source>
         <translation>Créneau</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="669"/>
+        <location filename="../lfowidget.cpp" line="747"/>
         <source>Custom</source>
         <translation>Libre</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="842"/>
+        <location filename="../lfowidget.cpp" line="948"/>
         <source>Delete "%1"?</source>
         <translation>Supprimer "%1"?</translation>
     </message>
     <message>
-        <location filename="../lfowidget.cpp" line="861"/>
+        <location filename="../lfowidget.cpp" line="967"/>
         <source>New Name</source>
         <translation>Nouveau nom</translation>
     </message>
@@ -519,42 +547,42 @@ Molette pour changer l'offset</translation>
 <context>
     <name>LogWidget</name>
     <message>
-        <location filename="../logwidget.cpp" line="27"/>
+        <location filename="../logwidget.cpp" line="49"/>
         <source>&Enable Log</source>
         <translation>&Activer le journal</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="33"/>
+        <location filename="../logwidget.cpp" line="55"/>
         <source>Log &MIDI Clock</source>
         <translation>Rapporter l'horologe &MIDI</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="38"/>
+        <location filename="../logwidget.cpp" line="60"/>
         <source>&Clear</source>
         <translation>&Vider</translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="91"/>
+        <location filename="../logwidget.cpp" line="113"/>
         <source>MIDI Clock</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="96"/>
+        <location filename="../logwidget.cpp" line="118"/>
         <source>MIDI Start (Transport)</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="100"/>
+        <location filename="../logwidget.cpp" line="122"/>
         <source>MIDI Continue (Transport)</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="104"/>
+        <location filename="../logwidget.cpp" line="126"/>
         <source>MIDI Stop (Transport)</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../logwidget.cpp" line="108"/>
+        <location filename="../logwidget.cpp" line="130"/>
         <source>Unknown event type</source>
         <translation>Evênement inconnu</translation>
     </message>
@@ -562,7 +590,7 @@ Molette pour changer l'offset</translation>
 <context>
     <name>MainWindow</name>
     <message>
-        <location filename="../mainwindow.cpp" line="58"/>
+        <location filename="../mainwindow.cpp" line="81"/>
         <source>Event Log</source>
         <translation>Journal</translation>
     </message>
@@ -571,28 +599,28 @@ Molette pour changer l'offset</translation>
         <translation type="obsolete">Paramètres</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="73"/>
+        <location filename="../mainwindow.cpp" line="96"/>
         <source>Groove</source>
         <translation>Groove</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="93"/>
+        <location filename="../mainwindow.cpp" line="116"/>
         <source>&New LFO...</source>
         <translation>&Nouveau LFO...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="98"/>
+        <location filename="../mainwindow.cpp" line="121"/>
         <source>&New Sequencer...</source>
         <translation>&Nouveau Séquenceur...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="99"/>
+        <location filename="../mainwindow.cpp" line="122"/>
         <source>Ctrl+T</source>
         <comment>Module|New Sequencer</comment>
         <translation>Ctrl+T</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="100"/>
+        <location filename="../mainwindow.cpp" line="123"/>
         <source>Add new Sequencer to tab bar</source>
         <translation>Ajouter un nouveau séquenceur</translation>
     </message>
@@ -613,185 +641,184 @@ Molette pour changer l'offset</translation>
         <translation type="obsolete">Supprimer ce module</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="104"/>
+        <location filename="../mainwindow.cpp" line="127"/>
         <source>&New</source>
         <translation>&Nouveau</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="106"/>
+        <location filename="../mainwindow.cpp" line="129"/>
         <source>Create new arpeggiator file</source>
         <translation>Créer un nouveau fichier d'arpège</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="109"/>
+        <location filename="../mainwindow.cpp" line="132"/>
         <source>&Open...</source>
         <translation>&Ouvrir...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="114"/>
+        <location filename="../mainwindow.cpp" line="137"/>
         <source>&Save</source>
         <translation>&Enregistrer</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="116"/>
+        <location filename="../mainwindow.cpp" line="139"/>
         <source>Save current arpeggiator file</source>
         <translation>Enregistrer les arpèges actuels dans un fichier</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="119"/>
+        <location filename="../mainwindow.cpp" line="142"/>
         <source>Save &as...</source>
         <translation>Enregistrer &sous...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="122"/>
+        <location filename="../mainwindow.cpp" line="145"/>
         <source>Save current arpeggiator file with new name</source>
         <translation>Enregistrer les arpèges actuels avec un nouveau nom</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="125"/>
+        <location filename="../mainwindow.cpp" line="148"/>
         <source>&Quit</source>
         <translation>&Quitter</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="126"/>
+        <location filename="../mainwindow.cpp" line="149"/>
         <source>Ctrl+Q</source>
         <comment>File|Quit</comment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="127"/>
+        <location filename="../mainwindow.cpp" line="150"/>
         <source>Quit application</source>
         <translation>Quitter l'application</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="130"/>
+        <location filename="../mainwindow.cpp" line="153"/>
         <source>&Run with internal clock</source>
         <translation>&Start/Stop avec horologe interne</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="154"/>
+        <location filename="../mainwindow.cpp" line="177"/>
         <source>&Connect to Jack Transport</source>
         <translation>Connecter à &Jack Transport</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="169"/>
+        <location filename="../mainwindow.cpp" line="192"/>
         <source>Ctrl+H</source>
         <comment>View|Event Log</comment>
         <translation>Ctrl+H</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="173"/>
+        <location filename="../mainwindow.cpp" line="196"/>
         <source>&Groove Settings</source>
         <translation>&Groove</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="174"/>
+        <location filename="../mainwindow.cpp" line="197"/>
         <source>Ctrl+G</source>
         <comment>View|Groove</comment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="185"/>
+        <location filename="../mainwindow.cpp" line="208"/>
         <source>Mod&ule</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="191"/>
+        <location filename="../mainwindow.cpp" line="214"/>
         <source>&Recently opened files</source>
         <translation>Fichiers &récemment ouverts</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="205"/>
+        <location filename="../mainwindow.cpp" line="228"/>
         <source>&MIDI Controllers...</source>
         <translation>&Contrôles MIDI...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="218"/>
+        <location filename="../mainwindow.cpp" line="241"/>
         <source>&File Toolbar</source>
         <translation>Barre d'outils &Fichier</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="226"/>
+        <location filename="../mainwindow.cpp" line="249"/>
         <source>&Control Toolbar</source>
         <translation>&Barre de contrôle</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="299"/>
-        <location filename="../mainwindow.cpp" line="312"/>
-        <location filename="../mainwindow.cpp" line="325"/>
+        <location filename="../mainwindow.cpp" line="321"/>
+        <location filename="../mainwindow.cpp" line="334"/>
+        <location filename="../mainwindow.cpp" line="347"/>
         <source>%1</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="311"/>
+        <location filename="../mainwindow.cpp" line="333"/>
         <source>Add MIDI LFO</source>
         <translation>Ajouter un LFO MIDI</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="324"/>
+        <location filename="../mainwindow.cpp" line="346"/>
         <source>Add Step Sequencer</source>
         <translation>Ajouter un séquenceur</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="547"/>
+        <location filename="../mainwindow.cpp" line="573"/>
         <source>QMidiArp XML files</source>
         <translation>Fichiers QMidiArp XML</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="548"/>
+        <location filename="../mainwindow.cpp" line="574"/>
         <source>Old QMidiArp files</source>
         <translation>Vieux fichiers texte QMidiArp</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="567"/>
-        <location filename="../mainwindow.cpp" line="723"/>
+        <location filename="../mainwindow.cpp" line="593"/>
+        <location filename="../mainwindow.cpp" line="749"/>
         <source>Could not read from file '%1'.</source>
         <translation>Erreur lors de la lecture du fichier '%1'.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="585"/>
+        <location filename="../mainwindow.cpp" line="611"/>
         <source>This is not a valid xml file for </source>
         <translation>Ceci n'est pas un fichier xml valide pour </translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="810"/>
+        <location filename="../mainwindow.cpp" line="836"/>
         <source>The QMidiArp text file was imported. If you save this file, it will be saved using the newer xml format under the name
  '%1'.</source>
         <translation>Le fichier texte QMidiArp a été importé. A la prochaine sauvegarde, il sera écrit au format xml actuel sous le nom
  '%1'.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="836"/>
-        <location filename="../mainwindow.cpp" line="922"/>
+        <location filename="../mainwindow.cpp" line="862"/>
         <source>Could not write to file '%1'.</source>
         <translation>Erreur lors de l'écriture du fichier '%1'.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="999"/>
+        <location filename="../mainwindow.cpp" line="968"/>
         <source>Unnamed file was changed.
 Save changes?</source>
         <translation>Le fichier sans nom a été modifié.
 Enregistrer les modifications?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1001"/>
+        <location filename="../mainwindow.cpp" line="970"/>
         <source>File '%1' was changed.
 Save changes?</source>
         <translation>Le fichier '%1' a été modifié.
 Enregistrer les modifications?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1005"/>
+        <location filename="../mainwindow.cpp" line="974"/>
         <source>Save changes</source>
         <translation>Enregistrer les modifications</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1135"/>
+        <location filename="../mainwindow.cpp" line="1104"/>
         <source>Could not read from resource file</source>
         <translation>Erreur de lecture du fichier ressources</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="88"/>
+        <location filename="../mainwindow.cpp" line="111"/>
         <source>&New Arp...</source>
         <translation>&Nouvel Arpège...</translation>
     </message>
@@ -800,43 +827,43 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">&Start/Stop</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="145"/>
+        <location filename="../mainwindow.cpp" line="168"/>
         <source>&Use incoming MIDI Clock</source>
         <translation>&Utiliser MIDI Clock entrant</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="168"/>
+        <location filename="../mainwindow.cpp" line="191"/>
         <source>&Event Log</source>
         <translation>&Journal</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="176"/>
+        <location filename="../mainwindow.cpp" line="199"/>
         <source>&Settings</source>
         <translation>&Paramètres</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="178"/>
+        <location filename="../mainwindow.cpp" line="201"/>
         <source>Ctrl+P</source>
         <comment>View|Settings</comment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="215"/>
+        <location filename="../mainwindow.cpp" line="238"/>
         <source>&About Qt...</source>
         <translation>&A propos de Qt...</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="284"/>
+        <location filename="../mainwindow.cpp" line="306"/>
         <source>About %1</source>
         <translation>A propos de %1</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="289"/>
+        <location filename="../mainwindow.cpp" line="311"/>
         <source>About Qt</source>
         <translation>A propos de Qt</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="298"/>
+        <location filename="../mainwindow.cpp" line="320"/>
         <source>Add MIDI Arpeggiator</source>
         <translation>Ajouter un arpège MIDI</translation>
     </message>
@@ -849,75 +876,75 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Supprimer "%1"?</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="111"/>
-        <location filename="../mainwindow.cpp" line="546"/>
+        <location filename="../mainwindow.cpp" line="134"/>
+        <location filename="../mainwindow.cpp" line="572"/>
         <source>Open arpeggiator file</source>
         <translation>Ouvrir un fichier arpège</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="583"/>
+        <location filename="../mainwindow.cpp" line="609"/>
         <source>Not a QMidiArp xml file.</source>
         <translation>Pas un fichier xml QMidiArp.</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="977"/>
+        <location filename="../mainwindow.cpp" line="946"/>
         <source>Save arpeggiator</source>
         <translation>Enregistrer l'arpège</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="977"/>
+        <location filename="../mainwindow.cpp" line="946"/>
         <source>QMidiArp files</source>
         <translation>Fichiers QMidiArp</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="1178"/>
+        <location filename="../mainwindow.cpp" line="1147"/>
         <source>Could not write to resource file</source>
         <translation>Erreur d'écriture du fichier ressources</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="90"/>
+        <location filename="../mainwindow.cpp" line="113"/>
         <source>Add new arpeggiator to tab bar</source>
         <translation>Ajouter un nouvel arpège à la barre d'onglets</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="95"/>
+        <location filename="../mainwindow.cpp" line="118"/>
         <source>Add new LFO to tab bar</source>
         <translation>Ajouter un nouveau LFO à la barre d'onglets</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="89"/>
+        <location filename="../mainwindow.cpp" line="112"/>
         <source>Ctrl+A</source>
         <comment>Module|New Arp</comment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="94"/>
+        <location filename="../mainwindow.cpp" line="117"/>
         <source>Ctrl+L</source>
         <comment>Module|New LFO</comment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="140"/>
+        <location filename="../mainwindow.cpp" line="163"/>
         <source>Tempo of internal clock</source>
         <translation>Tempo de l'horologe interne</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="183"/>
+        <location filename="../mainwindow.cpp" line="206"/>
         <source>&File</source>
         <translation>&Fichier</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="184"/>
+        <location filename="../mainwindow.cpp" line="207"/>
         <source>&View</source>
         <translation>&Affichage</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="186"/>
+        <location filename="../mainwindow.cpp" line="209"/>
         <source>&Help</source>
         <translation>&Aide</translation>
     </message>
     <message>
-        <location filename="../mainwindow.cpp" line="213"/>
+        <location filename="../mainwindow.cpp" line="236"/>
         <source>&About %1...</source>
         <translation>&A propos de %1...</translation>
     </message>
@@ -925,42 +952,42 @@ Enregistrer les modifications?</translation>
 <context>
     <name>MidiCCTable</name>
     <message>
-        <location filename="../midicctable.cpp" line="44"/>
+        <location filename="../midicctable.cpp" line="48"/>
         <source>Re&move</source>
         <translation>&Supprimer</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="45"/>
+        <location filename="../midicctable.cpp" line="49"/>
         <source>Re&vert</source>
         <translation>&Rétablir</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="58"/>
+        <location filename="../midicctable.cpp" line="62"/>
         <source>MIDI Controllers - </source>
         <translation>Contrôles MIDI - </translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="113"/>
+        <location filename="../midicctable.cpp" line="117"/>
         <source>Control</source>
         <translation>Contrôle</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="117"/>
+        <location filename="../midicctable.cpp" line="121"/>
         <source>CC#</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="121"/>
+        <location filename="../midicctable.cpp" line="125"/>
         <source>Ch</source>
         <translation>Can</translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="125"/>
+        <location filename="../midicctable.cpp" line="129"/>
         <source>min</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../midicctable.cpp" line="129"/>
+        <location filename="../midicctable.cpp" line="133"/>
         <source>max</source>
         <translation></translation>
     </message>
@@ -968,22 +995,22 @@ Enregistrer les modifications?</translation>
 <context>
     <name>PassWidget</name>
     <message>
-        <location filename="../passwidget.cpp" line="14"/>
+        <location filename="../passwidget.cpp" line="40"/>
         <source>&Forward unmatched events to port</source>
         <translation>&Acheminer les évênements non-traités au port</translation>
     </message>
     <message>
-        <location filename="../passwidget.cpp" line="38"/>
+        <location filename="../passwidget.cpp" line="63"/>
         <source>&Compact module layout style</source>
         <translation>Style &compact d'affichage des modules</translation>
     </message>
     <message>
-        <location filename="../passwidget.cpp" line="32"/>
+        <location filename="../passwidget.cpp" line="57"/>
         <source>&Modules controllable by MIDI controller</source>
         <translation>&Modules contrôlables par MIDI</translation>
     </message>
     <message>
-        <location filename="../passwidget.cpp" line="57"/>
+        <location filename="../passwidget.cpp" line="82"/>
         <source>Settings - </source>
         <translation>Paramètres - </translation>
     </message>
@@ -1003,92 +1030,134 @@ Enregistrer les modifications?</translation>
 <context>
     <name>SeqWidget</name>
     <message>
-        <location filename="../seqwidget.cpp" line="70"/>
+        <location filename="../seqwidget.cpp" line="92"/>
         <source>Transpose the sequence following incoming notes</source>
         <translation>Transposer la séquence suivant les notes entrants</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="105"/>
+        <location filename="../seqwidget.cpp" line="162"/>
         <source>Output</source>
         <translation>Sortie</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="107"/>
+        <location filename="../seqwidget.cpp" line="164"/>
         <source>&Mute</source>
         <translation>M&uet</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="116"/>
-        <location filename="../seqwidget.cpp" line="245"/>
-        <location filename="../seqwidget.cpp" line="262"/>
+        <location filename="../seqwidget.cpp" line="170"/>
+        <location filename="../seqwidget.cpp" line="253"/>
+        <location filename="../seqwidget.cpp" line="314"/>
+        <location filename="../seqwidget.cpp" line="333"/>
         <source>MIDI &Learn</source>
         <translation>&Apprendre du MIDI</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="110"/>
+        <location filename="../seqwidget.cpp" line="59"/>
         <source>Cancel MIDI &Learning</source>
         <translation>&Annuler l'apprentissage</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="119"/>
-        <location filename="../seqwidget.cpp" line="248"/>
-        <location filename="../seqwidget.cpp" line="265"/>
+        <location filename="../seqwidget.cpp" line="94"/>
+        <source>&Note Off</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="98"/>
+        <source>Stop output when Note is released</source>
+        <translation>Arrêter l'envoi de notes quand la touche est relâchée</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="106"/>
+        <source>&Restart</source>
+        <translation>&Redémarre</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="110"/>
+        <source>Restart sequence when a new note is received</source>
+        <translation>Redémarrer la séquence au départ quand une nouvelle note est reçue</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="112"/>
+        <source>&Trigger</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="116"/>
+        <source>Retrigger sequence when a new note is received</source>
+        <translation>Déclencher la séquence avec le timing des notes reçues</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="118"/>
+        <source>&Loop</source>
+        <translation>&Boucle</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="122"/>
+        <source>Play sequence as loop instead of a single run</source>
+        <translation>Jouer la séquence en boucle au lieu d'un passage unique</translation>
+    </message>
+    <message>
+        <location filename="../seqwidget.cpp" line="175"/>
+        <location filename="../seqwidget.cpp" line="258"/>
+        <location filename="../seqwidget.cpp" line="319"/>
+        <location filename="../seqwidget.cpp" line="338"/>
         <source>MIDI &Forget</source>
         <translation>&Oublier les contrôleurs MIDI</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="167"/>
+        <location filename="../seqwidget.cpp" line="221"/>
         <source>Sequence</source>
         <translation>Séquence</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="178"/>
+        <location filename="../seqwidget.cpp" line="232"/>
         <source>&Sequence</source>
         <translation>&Séquence</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="184"/>
+        <location filename="../seqwidget.cpp" line="238"/>
         <source>Preset Number</source>
         <translation>Index Preset</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="189"/>
-        <location filename="../seqwidget.cpp" line="190"/>
+        <location filename="../seqwidget.cpp" line="243"/>
+        <location filename="../seqwidget.cpp" line="244"/>
         <source>Re&cord</source>
         <translation>&Enregistrer</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="191"/>
+        <location filename="../seqwidget.cpp" line="245"/>
         <source>Record step by step</source>
         <translation>Enregistrer pas à pas les notes de l'entrée</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="208"/>
+        <location filename="../seqwidget.cpp" line="275"/>
         <source>Resolution (notes/beat): Number of notes produced every beat</source>
         <translation>Résolution (notes/beat): Nombre de notes produites à chaque quart de mesure</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="220"/>
+        <location filename="../seqwidget.cpp" line="287"/>
         <source>Length of Sequence in beats</source>
         <translation>Durée de la séquence en quarts de mesure</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="227"/>
+        <location filename="../seqwidget.cpp" line="294"/>
         <source>C&opy to new wave</source>
         <translation>C&opier dans la forme d'onde libre</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="241"/>
+        <location filename="../seqwidget.cpp" line="308"/>
         <source>Veloc&ity</source>
         <translation>Véloc&ité</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="881"/>
+        <location filename="../seqwidget.cpp" line="966"/>
         <source>Delete "%1"?</source>
         <translation>Supprimer "%1"?</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="900"/>
+        <location filename="../seqwidget.cpp" line="985"/>
         <source>New Name</source>
         <translation>Nouveau nom</translation>
     </message>
@@ -1101,52 +1170,52 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Vélo&cité</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="258"/>
+        <location filename="../seqwidget.cpp" line="328"/>
         <source>N&ote Length</source>
         <translation>Du&rée des Notes</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="72"/>
+        <location filename="../seqwidget.cpp" line="100"/>
         <source>&Velocity</source>
         <translation>&Vélocité</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="47"/>
+        <location filename="../seqwidget.cpp" line="69"/>
         <source>&Rename...</source>
         <translation>&Renommer...</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="48"/>
+        <location filename="../seqwidget.cpp" line="70"/>
         <source>Rename this Sequencer</source>
         <translation>Renommer ce séquenceur</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="53"/>
+        <location filename="../seqwidget.cpp" line="75"/>
         <source>&Delete...</source>
         <translation>&Supprimer...</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="54"/>
+        <location filename="../seqwidget.cpp" line="76"/>
         <source>Delete this Sequencer</source>
         <translation>Supprimer ce séquenceur</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="64"/>
+        <location filename="../seqwidget.cpp" line="86"/>
         <source>Input</source>
         <translation>Entrée</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="66"/>
+        <location filename="../seqwidget.cpp" line="88"/>
         <source>&Note</source>
         <translation>&Note</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="76"/>
+        <location filename="../seqwidget.cpp" line="104"/>
         <source>Set sequence velocity to that of incoming notes</source>
         <translation>La vélocité de la séquence suit celle des notes à l'entrée</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="81"/>
+        <location filename="../seqwidget.cpp" line="131"/>
         <source>&Channel</source>
         <translation>&Canal</translation>
     </message>
@@ -1155,7 +1224,7 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">&Durée des notes</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="275"/>
+        <location filename="../seqwidget.cpp" line="346"/>
         <source>&Transpose</source>
         <translation>&Transposer</translation>
     </message>
@@ -1168,12 +1237,12 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Numéro du contrôleur envoyé à la sortie</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="128"/>
+        <location filename="../seqwidget.cpp" line="183"/>
         <source>&Port</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="135"/>
+        <location filename="../seqwidget.cpp" line="189"/>
         <source>C&hannel</source>
         <translation>Cana&l</translation>
     </message>
@@ -1182,7 +1251,7 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Forme</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="171"/>
+        <location filename="../seqwidget.cpp" line="225"/>
         <source>Right button to mute points, left button to draw custom wave</source>
         <translation>Bouton droit de la souris pour rendre muet chaque point, bouton gauche pour dessiner la forme d'onde</translation>
     </message>
@@ -1203,7 +1272,7 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Fréquence (cycles/beat): Nombre de cycles d'onde produits à chaque quart de temps</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="198"/>
+        <location filename="../seqwidget.cpp" line="265"/>
         <source>&Resolution</source>
         <translation>&Résolution</translation>
     </message>
@@ -1212,7 +1281,7 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Résolution (év/beat): Nombre d'évênements produits à chaque quart de temps</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="213"/>
+        <location filename="../seqwidget.cpp" line="280"/>
         <source>&Length</source>
         <translation>&Durée</translation>
     </message>
@@ -1241,7 +1310,7 @@ Enregistrer les modifications?</translation>
         <translation type="obsolete">Créneau</translation>
     </message>
     <message>
-        <location filename="../seqwidget.cpp" line="696"/>
+        <location filename="../seqwidget.cpp" line="757"/>
         <source>Custom</source>
         <translation>Libre</translation>
     </message>

-- 
qmidiarp packaging



More information about the pkg-multimedia-commits mailing list