[SCM] drumkv1/master: New upstream version 0.8.2

umlaeute at users.alioth.debian.org umlaeute at users.alioth.debian.org
Tue Jun 20 09:12:08 UTC 2017


The following commit has been merged in the master branch:
commit 0136b6762290c8f45a872df34a614ce1c1b0dfba
Author: IOhannes m zmölnig <zmoelnig at umlautQ.umlaeute.mur.at>
Date:   Tue Jun 20 10:39:48 2017 +0200

    New upstream version 0.8.2

diff --git a/ChangeLog b/ChangeLog
index 9f232ef..2dcc199 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,6 +4,39 @@ drumkv1 - an old-school drum-kit sampler
 ChangeLog
 
 
+0.8.2  2017-05-02  Pre-LAC2017 release frenzy.
+
+- A custom knob/spin-box behavioral option have been added:
+  Configure/Knob edit mode, as to avoid abrupt changes upon
+  editing values (still the default behavior) and only take
+  effect (Deferred) when enter is pressed or the spin-box
+  loses focus.
+- The main GUI has been partially revamped, after replacing
+  some rotary knob/dial combos with kinda more skeuomorphic
+  fake-LED radio-buttons or check-boxes.
+- A MIDI In(put) status fake-LED is now featured on the
+  bottom-left status bar, adding up to eye-candy as usual;
+  also, each drum element key/sample now have their own
+  fake-LED flashing on respective MIDI note-on/off events.
+- A brand new and specific user preference option is now
+  available as Help/Configure.../Options/Use GM standard
+  drum names (default being yes/true/on).
+
+
+0.8.1  2017-03-21  A Spring'17 release.
+
+- Fixed a probable old miss about changing spin-box and
+  drop-down list not reflecting changes immediately into
+  the respective parameter dial knobs.
+- Fixed middle-button clicking on the dial-knobs to reset
+  to the current default parameter value.
+- Help/Configure.../Options/Use desktop environment native
+  dialogs option is now set initially off by default.
+- Added French man page (by Olivier Humbert, thanks).
+- Make builds reproducible byte for byte, by getting rid of
+  the configure build date and time stamps.
+
+
 0.8.0  2016-11-17  A Fall'16 release.
 
 - LV2_STATE__StateChanged is now transmitted as a regular
diff --git a/Makefile.in b/Makefile.in
index 412b786..b9061a0 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -37,7 +37,7 @@ headers_ui = \
 	src/$(name)widget.h \
 	src/$(name)widget_env.h \
 	src/$(name)widget_filt.h \
-	src/$(name)widget_knob.h \
+	src/$(name)widget_param.h \
 	src/$(name)widget_preset.h \
 	src/$(name)widget_status.h \
 	src/$(name)widget_sample.h \
@@ -52,7 +52,7 @@ sources_ui = \
 	src/$(name)widget.cpp \
 	src/$(name)widget_env.cpp \
 	src/$(name)widget_filt.cpp \
-	src/$(name)widget_knob.cpp \
+	src/$(name)widget_param.cpp \
 	src/$(name)widget_preset.cpp \
 	src/$(name)widget_status.cpp \
 	src/$(name)widget_sample.cpp \
@@ -141,8 +141,8 @@ install_lv2:	lv2
 install_jack:	jack $(name).1
 	@$(MAKE) INSTALL_ROOT=$(DESTDIR) -f $(name_jack).mak install
 	@install -d -v -m 0755 $(DESTDIR)$(mandir)/man1
-	@install -v -m 0644 $(name).1 $(DESTDIR)$(mandir)/man1
-	@gzip -vf $(DESTDIR)$(mandir)/man1/$(name).1
+	@install -v -m 0644 $(name)*.1 $(DESTDIR)$(mandir)/man1
+	@gzip -vf $(DESTDIR)$(mandir)/man1/$(name)*.1
 
 
 uninstall_core:	core
@@ -153,7 +153,7 @@ uninstall_lv2:	lv2
 
 uninstall_jack:	jack
 	@$(MAKE) INSTALL_ROOT=$(DESTDIR) -f $(name_jack).mak uninstall
-	@rm -vf $(DESTDIR)$(mandir)/man1/$(name).*
+	@rm -vf $(DESTDIR)$(mandir)/man1/$(name)*.1.gz
 
 
 clean_core:	$(name).mak
diff --git a/README b/README
index 5aebbf6..a5c18ca 100644
--- a/README
+++ b/README
@@ -80,7 +80,7 @@ installation:
 
   - if checking out from git, prepare the configure script with:
 
-    make -f Makefile.git
+    ./autogen.sh
 
 
 acknowledgements:
diff --git a/configure b/configure
index 15c1f7d..7645334 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.69 for drumkv1 0.8.0.
+# Generated by GNU Autoconf 2.69 for drumkv1 0.8.2.
 #
 # Report bugs to <rncbc at rncbc.org>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='drumkv1'
 PACKAGE_TARNAME='drumkv1'
-PACKAGE_VERSION='0.8.0'
-PACKAGE_STRING='drumkv1 0.8.0'
+PACKAGE_VERSION='0.8.2'
+PACKAGE_STRING='drumkv1 0.8.2'
 PACKAGE_BUGREPORT='rncbc at rncbc.org'
 PACKAGE_URL=''
 
@@ -1306,7 +1306,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 drumkv1 0.8.0 to adapt to many kinds of systems.
+\`configure' configures drumkv1 0.8.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1367,7 +1367,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of drumkv1 0.8.0:";;
+     short | recursive ) echo "Configuration of drumkv1 0.8.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1497,7 +1497,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-drumkv1 configure 0.8.0
+drumkv1 configure 0.8.2
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1874,7 +1874,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 drumkv1 $as_me 0.8.0, which was
+It was created by drumkv1 $as_me 0.8.2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2229,30 +2229,20 @@ ac_config_headers="$ac_config_headers src/config.h"
 ac_config_files="$ac_config_files Makefile drumkv1.spec src/src_core.pri src/src_jack.pri src/src_lv2.pri src/drumkv1.desktop"
 
 
-# Build date and time.
-if ${ac_cv_build_date+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_cv_build_date=$(date +"%b %d %Y %H:%M %z")
-fi
-
-ac_build_date="$ac_cv_build_date"
-
-cat >>confdefs.h <<_ACEOF
-#define CONFIG_BUILD_DATE "$ac_build_date"
-_ACEOF
-
-
 # Build version string.
 if ${ac_cv_build_version+:} false; then :
   $as_echo_n "(cached) " >&6
 else
 
-   ac_cv_build_version=$(git describe --tags --abbrev=6 2>/dev/null)
+   ac_cv_build_version=$(git describe --tags --dirty --abbrev=6 2>/dev/null)
    if test -n "$ac_cv_build_version"; then
-      ac_cv_build_version=$(echo $ac_cv_build_version | sed -r 's/^[^_]+_//')
-      ac_cv_build_version=$(echo $ac_cv_build_version | sed -r 's/-g/git./')
-      ac_cv_build_version=$(echo $ac_cv_build_version | sed -r 's/[_|-]+/./g')
+      ac_cv_build_version=$(echo $ac_cv_build_version | sed 's/^[^_]*[_v]//i')
+      ac_cv_build_version=$(echo $ac_cv_build_version | sed 's/-g/git./')
+      ac_cv_build_version=$(echo $ac_cv_build_version | sed 's/[_|-]\+/./g')
+      ac_cv_build_version_extra=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
+      if test "x$ac_cv_build_version_extra" != "xmaster"; then
+         ac_cv_build_version="$ac_cv_build_version [$ac_cv_build_version_extra]"
+      fi
    else
       ac_cv_build_version=$PACKAGE_VERSION
    fi
@@ -4868,6 +4858,9 @@ $as_echo "#define CONFIG_JACK 1" >>confdefs.h
 
       ac_jack_cflags="$ac_jack_cflags $JACK_CFLAGS"
       ac_jack_libs="$ac_jack_libs $JACK_LIBS"
+      CFLAGS="$CFLAGS $JACK_CFLAGS"
+      CPPFLAGS="$CPPFLAGS $JACK_CFLAGS"
+      LIBS="$LIBS $JACK_LIBS"
    else
       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: *** JACK library not found." >&5
 $as_echo "$as_me: WARNING: *** JACK library not found." >&2;}
@@ -6335,7 +6328,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 drumkv1 $as_me 0.8.0, which was
+This file was extended by drumkv1 $as_me 0.8.2, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -6397,7 +6390,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="\\
-drumkv1 config.status 0.8.0
+drumkv1 config.status 0.8.2
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -7112,7 +7105,7 @@ fi
 # Output summary message
 
 echo
-echo "  $PACKAGE_NAME $ac_build_version  ($ac_build_date)"
+echo "  $PACKAGE_NAME $ac_build_version"
 echo
 echo "  Build target . . . . . . . . . . . . . . . . . . .: $ac_debug"
 echo
diff --git a/configure.ac b/configure.ac
index 791de8d..6a880c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,23 +1,21 @@
 # Process this file with autoconf to produce a configure script.
-AC_INIT(drumkv1, 0.8.0, rncbc at rncbc.org)
+AC_INIT(drumkv1, 0.8.2, rncbc at rncbc.org)
 
 AC_CONFIG_SRCDIR(src/drumkv1.cpp)
 AC_CONFIG_HEADERS(src/config.h)
 AC_CONFIG_FILES(Makefile drumkv1.spec src/src_core.pri src/src_jack.pri src/src_lv2.pri src/drumkv1.desktop)
 
-# Build date and time.
-AC_CACHE_VAL([ac_cv_build_date],
-   [ac_cv_build_date=$(date +"%b %d %Y %H:%M %z")])
-ac_build_date="$ac_cv_build_date"
-AC_DEFINE_UNQUOTED(CONFIG_BUILD_DATE, ["$ac_build_date"], [Build date and time.])
-
 # Build version string.
 AC_CACHE_VAL([ac_cv_build_version], [
-   ac_cv_build_version=$(git describe --tags --abbrev=6 2>/dev/null)
+   ac_cv_build_version=$(git describe --tags --dirty --abbrev=6 2>/dev/null)
    if test -n "$ac_cv_build_version"; then
-      ac_cv_build_version=$(echo $ac_cv_build_version | sed -r 's/^[[^_]]+_//')
-      ac_cv_build_version=$(echo $ac_cv_build_version | sed -r 's/-g/git./')
-      ac_cv_build_version=$(echo $ac_cv_build_version | sed -r 's/[[_|-]]+/./g')
+      ac_cv_build_version=$(echo $ac_cv_build_version | sed 's/^[[^_]]*[[_v]]//i')
+      ac_cv_build_version=$(echo $ac_cv_build_version | sed 's/-g/git./')
+      ac_cv_build_version=$(echo $ac_cv_build_version | sed 's/[[_|-]]\+/./g')
+      ac_cv_build_version_extra=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
+      if test "x$ac_cv_build_version_extra" != "xmaster"; then
+         ac_cv_build_version="$ac_cv_build_version [[$ac_cv_build_version_extra]]"
+      fi
    else
       ac_cv_build_version=$PACKAGE_VERSION
    fi
@@ -428,6 +426,9 @@ if test "x$ac_jack" = "xyes"; then
       AC_DEFINE(CONFIG_JACK, 1, [Define if JACK library is available.])
       ac_jack_cflags="$ac_jack_cflags $JACK_CFLAGS"
       ac_jack_libs="$ac_jack_libs $JACK_LIBS"
+      CFLAGS="$CFLAGS $JACK_CFLAGS"
+      CPPFLAGS="$CPPFLAGS $JACK_CFLAGS"
+      LIBS="$LIBS $JACK_LIBS"
    else
       AC_MSG_WARN([*** JACK library not found.])
       AC_MSG_WARN([*** JACK stand-alone build will be disabled.])
@@ -683,7 +684,7 @@ AC_OUTPUT
 # Output summary message
 
 echo
-echo "  $PACKAGE_NAME $ac_build_version  ($ac_build_date)"
+echo "  $PACKAGE_NAME $ac_build_version"
 echo
 echo "  Build target . . . . . . . . . . . . . . . . . . .: $ac_debug"
 echo
diff --git a/drumkv1.1 b/drumkv1.1
index 202db72..4d79511 100644
--- a/drumkv1.1
+++ b/drumkv1.1
@@ -9,7 +9,7 @@ This manual page documents briefly the
 .B drumkv1_jack
 command.
 .PP
-\fBdrumkv1\fP an old-school drum-kit sampler synthesizer with stereo fx.
+\fBdrumkv1\fP is an old-school drum-kit sampler synthesizer with stereo fx.
 .PP
 features:
 .IP
diff --git a/drumkv1.fr.1 b/drumkv1.fr.1
new file mode 100644
index 0000000..0b6fbb0
--- /dev/null
+++ b/drumkv1.fr.1
@@ -0,0 +1,42 @@
+.TH DRUMKV1 "1" "Juin 17, 2014"
+.SH NOM
+drumkv1 \- un échantillonneur de kit de batterie à l'ancienne
+.SH SYNOPSIS
+.B drumkv1_jack
+[\fIoptions\fR] [\fIfichier-de-pré-réglage\fR]
+.SH DESCRIPTION
+Cette page de manuel documente rapidement la commande
+.B drumkv1_jack
+.
+.PP
+\fBdrumkv1\fP est un synthétiseur échantillonneur de kit de batterie à
+l'ancienne, avec des effets stéréo.
+.PP
+fonctionnalités :
+.IP
+un client JACK autonome pur avec JACK-session, NSM, et un support d'entrée
+JACK MIDI ainsi que ALSA MIDI;
+.IP
+un greffon d'instrument LV2.
+URI : http://drumkv1.sourceforge.net/lv2
+.SH OPTIONS
+.HP
+\fB\-g\fR, \fB\-\-no\-gui\fR
+.IP
+Désactive l'interface graphique utilisateur
+.HP
+\fB\-h\fR, \fB\-\-help\fR
+.IP
+Affiche de l'aide à propos des options de ligne de commande
+.HP
+\fB\-v\fR, \fB\-\-version\fR
+.IP
+Affiche des informations de version
+.SH FICHIERS
+Les paramètres de configuration sont stockés dans ~/.config/rncbc.org/drumkv1.conf
+.SH AUTEUR
+drumkv1 a été écrit par Rui Nuno Capela.
+.PP
+La version française de cette page de manuel a été traduite par Olivier Humbert
+<trebmuh at tuxfamily.org>, pour le projet LibraZiK (mais peut être utilisée par
+d'autres).
diff --git a/drumkv1.spec.in b/drumkv1.spec.in
index 50e0aa5..153bde5 100644
--- a/drumkv1.spec.in
+++ b/drumkv1.spec.in
@@ -1,6 +1,6 @@
 %define name    @PACKAGE_TARNAME@
 %define version @PACKAGE_VERSION@
-%define release 26
+%define release 28
 
 %define _prefix	@ac_prefix@
 
@@ -50,7 +50,7 @@ BuildRequires:	lv2-devel
 %__make DESTDIR="%{buildroot}" install
 
 %clean
-[ -d "%{buildroot}" -a "%{buildroot}" != "" ] && %__rm -rf "%{buildroot}"
+[ -d "%{buildroot}" -a "%{buildroot}" != "/" ] && %__rm -rf "%{buildroot}"
 
 %files
 %defattr(-,root,root)
@@ -84,9 +84,13 @@ BuildRequires:	lv2-devel
 %{_datadir}/icons/hicolor/32x32/mimetypes/application-x-%{name}*.png
 %{_datadir}/icons/hicolor/scalable/apps/%{name}.svg
 %{_datadir}/icons/hicolor/scalable/mimetypes/application-x-%{name}*.svg
-%{_datadir}/man/man1/%{name}.1.gz
+%{_datadir}/man/man1/%{name}*.1.gz
 
 %changelog
+* Tue May  2 2017 Rui Nuno Capela <rncbc at rncbc.org> 0.8.2
+- Pre-LAC2017 release frenzy.
+* Tue Mar 21 2017 Rui Nuno Capela <rncbc at rncbc.org> 0.8.1
+- A Spring'17 release.
 * Thu Nov 17 2016 Rui Nuno Capela <rncbc at rncbc.org> 0.8.0
 - A Fall'16 release.
 * Mon Sep 19 2016 Rui Nuno Capela <rncbc at rncbc.org> 0.7.6
diff --git a/src/config.h.in b/src/config.h.in
index 547a1fc..60ccf51 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -6,9 +6,6 @@
 /* Default executable binary path. */
 #undef CONFIG_BINDIR
 
-/* Build date and time. */
-#undef CONFIG_BUILD_DATE
-
 /* Build version string. */
 #undef CONFIG_BUILD_VERSION
 
diff --git a/src/drumkv1.cpp b/src/drumkv1.cpp
index fbfd991..a66c2f6 100644
--- a/src/drumkv1.cpp
+++ b/src/drumkv1.cpp
@@ -1,7 +1,7 @@
 // drumkv1.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -38,6 +38,8 @@
 #include "drumkv1_controls.h"
 #include "drumkv1_programs.h"
 
+#include "drumkv1_sched.h"
+
 
 #ifdef CONFIG_DEBUG_0
 #include <stdio.h>
@@ -623,6 +625,11 @@ public:
 
 	drumkv1_element element;
 
+	void reset();
+
+	void midiInEnabled(bool on);
+	uint32_t midiInCount();
+
 	drumkv1_sample  gen1_sample;
 	drumkv1_wave_lf lfo1_wave;
 
@@ -749,6 +756,40 @@ struct drumkv1_voice : public drumkv1_list<drumkv1_voice>
 };
 
 
+// MIDI input asynchronous status notification
+
+class drumkv1_midi_in : public drumkv1_sched
+{
+public:
+
+	drumkv1_midi_in (drumkv1 *pDrumk)
+		: drumkv1_sched(pDrumk, MidiIn),
+			m_enabled(false), m_count(0) {}
+
+	void schedule_event()
+		{ if (m_enabled && ++m_count < 2) schedule(-1); }
+	void schedule_note(int key, int vel)
+		{ if (m_enabled) schedule((vel << 7) | key); }
+
+	void process(int) {}
+
+	void enabled(bool on)
+		{ m_enabled = on; m_count = 0; }
+
+	uint32_t count()
+	{
+		const uint32_t ret = m_count;
+		m_count = 0;
+		return ret;
+	}
+
+private:
+
+	bool     m_enabled;
+	uint32_t m_count;
+};
+
+
 // synth engine implementation
 
 class drumkv1_impl
@@ -806,6 +847,9 @@ public:
 	void resetParamValues(bool bSwap);
 	void reset();
 
+	void midiInEnabled(bool on);
+	uint32_t midiInCount();
+
 protected:
 
 	void allSoundOff();
@@ -848,6 +892,7 @@ private:
 	drumkv1_config   m_config;
 	drumkv1_controls m_controls;
 	drumkv1_programs m_programs;
+	drumkv1_midi_in  m_midi_in;
 
 	uint16_t m_nchannels;
 	float    m_srate;
@@ -900,8 +945,8 @@ private:
 // synth engine constructor
 
 drumkv1_impl::drumkv1_impl (
-	drumkv1 *pDrumk, uint16_t nchannels, float srate )
-	: m_pDrumk(pDrumk), m_controls(pDrumk), m_programs(pDrumk), m_bpm(180.0f)
+	drumkv1 *pDrumk, uint16_t nchannels, float srate ) : m_pDrumk(pDrumk),
+		m_controls(pDrumk), m_programs(pDrumk), m_midi_in(pDrumk), m_bpm(180.0f)
 {
 	// allocate voice pool.
 	m_voices = new drumkv1_voice * [MAX_VOICES];
@@ -1475,6 +1520,7 @@ void drumkv1_impl::process_midi ( uint8_t *data, uint32_t size )
 					m_group[pv->group] = pv;
 				}
 			}
+			m_midi_in.schedule_note(key, value);
 		}
 		// note off
 		else if (status == 0x80 || (status == 0x90 && value == 0)) {
@@ -1489,6 +1535,7 @@ void drumkv1_impl::process_midi ( uint8_t *data, uint32_t size )
 					}
 				}
 			}
+			m_midi_in.schedule_note(key, 0);
 		}
 		// key pressure/poly.aftertouch
 		else if (status == 0xa0) {
@@ -1545,6 +1592,9 @@ void drumkv1_impl::process_midi ( uint8_t *data, uint32_t size )
 
 	// process pending controllers...
 	m_controls.process_dequeue();
+
+	// asynchronous event notification...
+	m_midi_in.schedule_event();
 }
 
 
@@ -1630,6 +1680,22 @@ void drumkv1_impl::resetParamValues ( bool bSwap )
 }
 
 
+// controllers accessor
+
+drumkv1_controls *drumkv1_impl::controls (void)
+{
+	return &m_controls;
+}
+
+
+// programs accessor
+
+drumkv1_programs *drumkv1_impl::programs (void)
+{
+	return &m_programs;
+}
+
+
 // all reset clear
 
 void drumkv1_impl::reset (void)
@@ -1670,22 +1736,19 @@ void drumkv1_impl::reset (void)
 }
 
 
-// controllers accessor
+// MIDI input asynchronous status notification accessors
 
-drumkv1_controls *drumkv1_impl::controls (void)
+void drumkv1_impl::midiInEnabled ( bool on )
 {
-	return &m_controls;
+	m_midi_in.enabled(on);
 }
 
-
-// programs accessor
-
-drumkv1_programs *drumkv1_impl::programs (void)
+uint32_t drumkv1_impl::midiInCount (void)
 {
-	return &m_programs;
+	return m_midi_in.count();
 }
 
-
+ 
 // synthesize
 
 void drumkv1_impl::process ( float **ins, float **outs, uint32_t nframes )
@@ -1942,7 +2005,6 @@ void drumkv1_impl::process ( float **ins, float **outs, uint32_t nframes )
 }
 
 
-
 //-------------------------------------------------------------------------
 // drumkv1 - decl.
 //
@@ -2309,4 +2371,19 @@ void drumkv1_element::resetParamValues ( bool bSwap )
 }
 
 
+// MIDI input asynchronous status notification accessors
+
+void drumkv1::midiInEnabled ( bool on )
+{
+	m_pImpl->midiInEnabled(on);
+}
+
+
+uint32_t drumkv1::midiInCount (void)
+{
+	return m_pImpl->midiInCount();
+}
+
+
 // end of drumkv1.cpp
+
diff --git a/src/drumkv1.desktop.in b/src/drumkv1.desktop.in
index 96a837c..3cf8223 100644
--- a/src/drumkv1.desktop.in
+++ b/src/drumkv1.desktop.in
@@ -3,7 +3,7 @@ Name=@PACKAGE_NAME@
 Version=1.0
 GenericName=MIDI
 Comment=drumkv1 is an old school drum-kit sampler
-Comment[fr]=drumkv1 est un échantillonneur de batterie de la vieille école
+Comment[fr]=drumkv1 est un échantillonneur de batterie à l'ancienne
 Exec=@ac_prefix@/bin/drumkv1_jack
 Icon=drumkv1
 Categories=Audio;AudioVideo;Midi;X-Alsa;X-Jack;Qt;
diff --git a/src/drumkv1.h b/src/drumkv1.h
index 1d05608..c9e2679 100644
--- a/src/drumkv1.h
+++ b/src/drumkv1.h
@@ -1,7 +1,7 @@
 // drumkv1.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -177,6 +177,9 @@ public:
 
 	virtual void updatePreset(bool bDirty) = 0;
 
+	void midiInEnabled(bool on);
+	uint32_t midiInCount();
+
 protected:
 
 	virtual void selectSample(int key) = 0;
@@ -225,3 +228,4 @@ private:
 #endif// __drumkv1_h
 
 // end of drumkv1.h
+
diff --git a/src/drumkv1.qrc b/src/drumkv1.qrc
index 5d96f20..e7578ef 100644
--- a/src/drumkv1.qrc
+++ b/src/drumkv1.qrc
@@ -1,5 +1,7 @@
 <RCC version="1.0">
 	<qresource>
+		<file>images/ledOn.png</file>
+		<file>images/ledOff.png</file>
 		<file>images/presetNew.png</file>
 		<file>images/presetOpen.png</file>
 		<file>images/presetSave.png</file>
diff --git a/src/drumkv1_config.cpp b/src/drumkv1_config.cpp
index 1d4b237..18a3c1b 100644
--- a/src/drumkv1_config.cpp
+++ b/src/drumkv1_config.cpp
@@ -1,7 +1,7 @@
 // drumkv1_config.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -311,13 +311,15 @@ void drumkv1_config::load (void)
 	sPresetDir = QSettings::value("/PresetDir").toString();
 	sSampleDir = QSettings::value("/SampleDir").toString();
 	iKnobDialMode = QSettings::value("/KnobDialMode", 0).toInt();
+	iKnobEditMode = QSettings::value("/KnobEditMode", 0).toInt();
 	bControlsEnabled = QSettings::value("/ControlsEnabled", false).toBool();
 	bProgramsEnabled = QSettings::value("/ProgramsEnabled", false).toBool();
+	bUseGMDrumNames = QSettings::value("/UseGMDrumNames", true).toBool();
 	QSettings::endGroup();
 
 	QSettings::beginGroup("/Dialogs");
 	bProgramsPreview = QSettings::value("/ProgramsPreview", false).toBool();
-	bUseNativeDialogs = QSettings::value("/UseNativeDialogs", true).toBool();
+	bUseNativeDialogs = QSettings::value("/UseNativeDialogs", false).toBool();
 	// Run-time special non-persistent options.
 	bDontUseNativeDialogs = !bUseNativeDialogs;
 	QSettings::endGroup();
@@ -339,8 +341,10 @@ void drumkv1_config::save (void)
 	QSettings::setValue("/PresetDir", sPresetDir);
 	QSettings::setValue("/SampleDir", sSampleDir);
 	QSettings::setValue("/KnobDialMode", iKnobDialMode);
+	QSettings::setValue("/KnobEditMode", iKnobEditMode);
 	QSettings::setValue("/ControlsEnabled", bControlsEnabled);
 	QSettings::setValue("/ProgramsEnabled", bProgramsEnabled);
+	QSettings::setValue("/UseGMDrumNames", bUseGMDrumNames);
 	QSettings::endGroup();
 
 	QSettings::beginGroup("/Dialogs");
@@ -357,3 +361,4 @@ void drumkv1_config::save (void)
 
 
 // end of drumkv1_config.cpp
+
diff --git a/src/drumkv1_config.h b/src/drumkv1_config.h
index dd46054..576f2b9 100644
--- a/src/drumkv1_config.h
+++ b/src/drumkv1_config.h
@@ -1,7 +1,7 @@
 // drumkv1_config.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -28,7 +28,7 @@
 
 #define DRUMKV1_SUBTITLE    "an old-school drum-kit sampler."
 #define DRUMKV1_WEBSITE     "http://drumkv1.sourceforge.net"
-#define DRUMKV1_COPYRIGHT   "Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved."
+#define DRUMKV1_COPYRIGHT   "Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved."
 
 #define DRUMKV1_DOMAIN      "rncbc.org"
 
@@ -60,8 +60,9 @@ public:
 	QString sPresetDir;
 	QString sSampleDir;
 
-	// Dial knob behavior mode.
+	// Knob behavior modes.
 	int iKnobDialMode;
+	int iKnobEditMode;
 
 	// Special persistent options.
 	bool bControlsEnabled;
@@ -74,6 +75,9 @@ public:
 	// Custom widget style theme.
 	QString sCustomStyleTheme;
 
+	// Whether to display GM Standard drum-note/key names.
+	bool bUseGMDrumNames;
+
 	// Singleton instance accessor.
 	static drumkv1_config *getInstance();
 
@@ -122,3 +126,4 @@ private:
 #endif	// __drumkv1_config_h
 
 // end of drumkv1_config.h
+
diff --git a/src/drumkv1_controls.cpp b/src/drumkv1_controls.cpp
index f823fb8..4bde096 100644
--- a/src/drumkv1_controls.cpp
+++ b/src/drumkv1_controls.cpp
@@ -1,7 +1,7 @@
 // drumkv1_controls.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -234,7 +234,7 @@ public:
 			m_mask = new_size - 1;
 			m_events = new_events;
 			if (old_events)
-				delete old_events;
+				delete [] old_events;
 		}
 	}
 
diff --git a/src/drumkv1_controls.h b/src/drumkv1_controls.h
index 89df16d..653451c 100644
--- a/src/drumkv1_controls.h
+++ b/src/drumkv1_controls.h
@@ -1,7 +1,7 @@
 // drumkv1_controls.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1_jack.cpp b/src/drumkv1_jack.cpp
index 00d3906..8665c0d 100644
--- a/src/drumkv1_jack.cpp
+++ b/src/drumkv1_jack.cpp
@@ -1,7 +1,7 @@
 // drumkv1_jack.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -692,12 +692,11 @@ bool drumkv1_jack_application::parse_args (void)
 		}
 		else
 		if (sArg == "-v" || sArg == "-V" || sArg == "--version") {
-			out << QObject::tr("Qt: %1\n")
+			out << QString("Qt: %1\n")
 				.arg(qVersion());
-			out << QObject::tr("%1: %2  (%3)\n")
+			out << QString("%1: %2\n")
 				.arg(DRUMKV1_TITLE)
-				.arg(CONFIG_BUILD_VERSION)
-				.arg(CONFIG_BUILD_DATE);
+				.arg(CONFIG_BUILD_VERSION);
 			return false;
 		}
 	}
@@ -897,3 +896,4 @@ int main ( int argc, char *argv[] )
 
 
 // end of drumkv1_jack.cpp
+
diff --git a/src/drumkv1_jack.h b/src/drumkv1_jack.h
index 06e6fc9..13f4533 100644
--- a/src/drumkv1_jack.h
+++ b/src/drumkv1_jack.h
@@ -1,7 +1,7 @@
 // drumkv1_jack.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -174,3 +174,4 @@ private:
 #endif// __drumkv1_jack_h
 
 // end of drumkv1_jack.h
+
diff --git a/src/drumkv1_lv2.cpp b/src/drumkv1_lv2.cpp
index 6eef553..cbcf9c5 100644
--- a/src/drumkv1_lv2.cpp
+++ b/src/drumkv1_lv2.cpp
@@ -1,7 +1,7 @@
 // drumkv1_lv2.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -815,3 +815,4 @@ LV2_SYMBOL_EXPORT const LV2_Descriptor *lv2_descriptor ( uint32_t index )
 
 
 // end of drumkv1_lv2.cpp
+
diff --git a/src/drumkv1_lv2.h b/src/drumkv1_lv2.h
index a22606b..b3e15fb 100644
--- a/src/drumkv1_lv2.h
+++ b/src/drumkv1_lv2.h
@@ -1,7 +1,7 @@
 // drumkv1_lv2.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -147,3 +147,4 @@ private:
 #endif// __drumkv1_lv2_h
 
 // end of drumkv1_lv2.h
+
diff --git a/src/drumkv1_sample.cpp b/src/drumkv1_sample.cpp
index e0693fd..0ab1bf8 100644
--- a/src/drumkv1_sample.cpp
+++ b/src/drumkv1_sample.cpp
@@ -1,7 +1,7 @@
 // drumkv1_sample.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -37,29 +37,16 @@ public:
 
 	// ctor.
 	drumkv1_reverse_sched (drumkv1 *pDrumk, drumkv1_sample *sample)
-		: drumkv1_sched(pDrumk, Sample),
-			m_sample(sample), m_reverse(false) {}
-
-	// schedule reverse.
-	void reverse_sched(bool reverse)
-	{
-		m_reverse = reverse;
-
-		schedule();
-	}
+		: drumkv1_sched(pDrumk, Sample), m_sample(sample) {}
 
 	// process reverse (virtual).
 	void process(int)
-	{
-		m_sample->setReverse(m_reverse);
-	}
+		{ m_sample->reverse_sync(); }
 
 private:
 
 	// instance variables.
 	drumkv1_sample *m_sample;
-
-	bool m_reverse;
 };
 
 
@@ -131,7 +118,7 @@ bool drumkv1_sample::open ( const char *filename, float freq0 )
 	::sf_close(file);
 
 	if (m_reverse)
-		reverse_sample();
+		reverse_sync();
 
 	reset(freq0);
 
@@ -162,14 +149,14 @@ void drumkv1_sample::close (void)
 
 
 // schedule sample reverse.
-void drumkv1_sample::reverse_sched ( bool reverse )
+void drumkv1_sample::reverse_sched (void)
 {
-	m_reverse_sched->reverse_sched(reverse);
+	m_reverse_sched->schedule();
 }
 
 
 // reverse sample buffer.
-void drumkv1_sample::reverse_sample (void)
+void drumkv1_sample::reverse_sync (void)
 {
 	if (m_nframes > 0 && m_pframes) {
 		const uint32_t nsize1 = (m_nframes - 1);
diff --git a/src/drumkv1_sample.h b/src/drumkv1_sample.h
index 4932867..e65a1ef 100644
--- a/src/drumkv1_sample.h
+++ b/src/drumkv1_sample.h
@@ -1,7 +1,7 @@
 // drumkv1_sample.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -54,13 +54,7 @@ public:
 
 	// reverse mode.
 	void setReverse (bool reverse)
-	{
-		if (( m_reverse && !reverse) ||
-			(!m_reverse &&  reverse)) {
-			m_reverse = reverse;
-			reverse_sample();
-		}
-	}
+		{ reverse_test(reverse); }
 
 	bool isReverse() const
 		{ return m_reverse; }
@@ -70,11 +64,14 @@ public:
 	{
 		if (( m_reverse && !reverse) ||
 			(!m_reverse &&  reverse)) {
-			reverse_sched(reverse);
+			m_reverse = reverse;
+			reverse_sched();
 		}
 	}
 
-	void reverse_sched(bool reverse);
+	// reverse sample buffer.
+	void reverse_sched();
+	void reverse_sync();
 
 	// init.
 	bool open(const char *filename, float freq0 = 1.0f);
@@ -111,11 +108,6 @@ public:
 	bool isOver(uint32_t frame) const
 		{ return !m_pframes || (frame >= m_nframes); }
 
-protected:
-
-	// reverse sample buffer.
-	void reverse_sample();
-
 private:
 
 	// instance variables.
diff --git a/src/drumkv1_sched.cpp b/src/drumkv1_sched.cpp
index 354747f..8ac5023 100644
--- a/src/drumkv1_sched.cpp
+++ b/src/drumkv1_sched.cpp
@@ -1,7 +1,7 @@
 // drumkv1_sched.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1_sched.h b/src/drumkv1_sched.h
index d61fdc0..df098e5 100644
--- a/src/drumkv1_sched.h
+++ b/src/drumkv1_sched.h
@@ -1,7 +1,7 @@
 // drumkv1_sched.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -37,7 +37,7 @@ class drumkv1_sched
 public:
 
 	// plausible sched types.
-	enum Type { Sample, Programs, Controls, Controller };
+	enum Type { Sample, Programs, Controls, Controller, MidiIn };
 
 	// ctor.
 	drumkv1_sched(drumkv1 *pDrumk, Type stype, uint32_t nsize = 8);
diff --git a/src/drumkv1_ui.cpp b/src/drumkv1_ui.cpp
index 830bd30..8907ebe 100644
--- a/src/drumkv1_ui.cpp
+++ b/src/drumkv1_ui.cpp
@@ -1,7 +1,7 @@
 // drumkv1_ui.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -139,4 +139,16 @@ void drumkv1_ui::updatePreset ( bool bDirty )
 }
 
 
+void drumkv1_ui::midiInEnabled ( bool bEnabled )
+{
+	m_pDrumk->midiInEnabled(bEnabled);
+}
+
+
+uint32_t drumkv1_ui::midiInCount (void)
+{
+	return m_pDrumk->midiInCount();
+}
+
+
 // end of drumkv1_ui.cpp
diff --git a/src/drumkv1_ui.h b/src/drumkv1_ui.h
index 27f0180..bf471e8 100644
--- a/src/drumkv1_ui.h
+++ b/src/drumkv1_ui.h
@@ -1,7 +1,7 @@
 // drumkv1_ui.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -65,6 +65,9 @@ public:
 
 	void updatePreset(bool bDirty);
 
+	void midiInEnabled(bool bEnabled);
+	uint32_t midiInCount();
+
 private:
 
 	drumkv1 *m_pDrumk;
diff --git a/src/drumkv1widget.cpp b/src/drumkv1widget.cpp
index 77c8093..0dad557 100644
--- a/src/drumkv1widget.cpp
+++ b/src/drumkv1widget.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -30,6 +30,10 @@
 
 #include <QMessageBox>
 #include <QDir>
+#include <QTimer>
+
+#include <QShowEvent>
+#include <QHideEvent>
 
 
 //-------------------------------------------------------------------------
@@ -119,26 +123,30 @@ drumkv1widget::drumkv1widget ( QWidget *pParent, Qt::WindowFlags wflags )
 		channels << QString::number(iChannel + 1);
 
 	m_ui.Def1ChannelKnob->insertItems(0, channels);
-
+#if 0
 	// Noteoff modes.
 	QStringList modes;
 	modes << tr("Disabled");
 	modes << tr("Enabled");
 
 	m_ui.Def1NoteoffKnob->insertItems(0, modes);
-
+#else
+	m_ui.Def1NoteoffKnob->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
+#endif
 	// Dynamic states.
 	QStringList states;
 	states << tr("Off");
 	states << tr("On");
-
+#if 0
 	m_ui.Gen1ReverseKnob->insertItems(0, states);
 
 	m_ui.Lfo1SyncKnob->insertItems(0, states);
 
 	m_ui.Dyn1CompressKnob->insertItems(0, states);
 	m_ui.Dyn1LimiterKnob->insertItems(0, states);
-
+#else
+	m_ui.Lfo1SyncKnob->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
+#endif
 	// Special values
 	const QString& sOff = states.first();
 	m_ui.Cho1WetKnob->setSpecialValueText(sOff);
@@ -161,7 +169,7 @@ drumkv1widget::drumkv1widget ( QWidget *pParent, Qt::WindowFlags wflags )
 	m_ui.Gen1GroupKnob->setMinimum(0.0f);
 	m_ui.Gen1GroupKnob->setMaximum(128.0f);
 	m_ui.Gen1GroupKnob->setDecimals(0);
-	m_ui.Gen1GroupKnob->setSingleStep(10.0f);
+//	m_ui.Gen1GroupKnob->setSingleStep(10.0f);
 
 	// GEN octave limits.
 	m_ui.Gen1CoarseKnob->setMinimum(-4.0f);
@@ -179,7 +187,7 @@ drumkv1widget::drumkv1widget ( QWidget *pParent, Qt::WindowFlags wflags )
 	m_ui.Lfo1BpmKnob->setScale(1.0f);
 	m_ui.Lfo1BpmKnob->setMinimum(0.0f);
 	m_ui.Lfo1BpmKnob->setMaximum(360.0f);
-	m_ui.Lfo1BpmKnob->setSingleStep(1.0f);
+//	m_ui.Lfo1BpmKnob->setSingleStep(1.0f);
 	m_ui.Lfo1SweepKnob->setMinimum(-1.0f);
 	m_ui.Lfo1SweepKnob->setMaximum(+1.0f);
 	m_ui.Lfo1CutoffKnob->setMinimum(-1.0f);
@@ -205,7 +213,7 @@ drumkv1widget::drumkv1widget ( QWidget *pParent, Qt::WindowFlags wflags )
 	m_ui.Del1BpmKnob->setScale(1.0f);
 	m_ui.Del1BpmKnob->setMinimum(0.0f);
 	m_ui.Del1BpmKnob->setMaximum(360.0f);
-	m_ui.Del1BpmKnob->setSingleStep(1.0f);
+//	m_ui.Del1BpmKnob->setSingleStep(1.0f);
 
 	// GEN1
 	setParamKnob(drumkv1::GEN1_REVERSE, m_ui.Gen1ReverseKnob);
@@ -490,13 +498,15 @@ drumkv1widget::drumkv1widget ( QWidget *pParent, Qt::WindowFlags wflags )
 	if (pConfig) {
 		drumkv1widget_dial::setDialMode(
 			drumkv1widget_dial::DialMode(pConfig->iKnobDialMode));
+		drumkv1widget_edit::setEditMode(
+			drumkv1widget_edit::EditMode(pConfig->iKnobEditMode));
 	}
 
 	// Epilog.
 	// QWidget::adjustSize();
 
 	m_ui.StatusBar->showMessage(tr("Ready"), 5000);
-	m_ui.StatusBar->setModified(false);
+	m_ui.StatusBar->modified(false);
 	m_ui.Preset->setDirtyPreset(false);
 }
 
@@ -509,13 +519,11 @@ drumkv1widget::~drumkv1widget (void)
 }
 
 
-// Create/initialize the scheduler/work notifier.
-void drumkv1widget::initSchedNotifier (void)
+// Open/close the scheduler/work notifier.
+void drumkv1widget::openSchedNotifier (void)
 {
-	if (m_sched_notifier) {
-		delete m_sched_notifier;
-		m_sched_notifier = NULL;
-	}
+	if (m_sched_notifier)
+		return;
 
 	drumkv1_ui *pDrumkUi = ui_instance();
 	if (pDrumkUi == NULL)
@@ -526,29 +534,61 @@ void drumkv1widget::initSchedNotifier (void)
 	QObject::connect(m_sched_notifier,
 		SIGNAL(notify(int, int)),
 		SLOT(updateSchedNotify(int, int)));
+
+	pDrumkUi->midiInEnabled(true);
+}
+
+
+void drumkv1widget::closeSchedNotifier (void)
+{
+	if (m_sched_notifier) {
+		delete m_sched_notifier;
+		m_sched_notifier = NULL;
+	}
+
+	drumkv1_ui *pDrumkUi = ui_instance();
+	if (pDrumkUi)
+		pDrumkUi->midiInEnabled(false);
+}
+
+
+// Show/hide widget handlers.
+void drumkv1widget::showEvent ( QShowEvent *pShowEvent )
+{
+	QWidget::showEvent(pShowEvent);
+
+	openSchedNotifier();
+}
+
+
+void drumkv1widget::hideEvent ( QHideEvent *pHideEvent )
+{
+	closeSchedNotifier();
+
+	QWidget::hideEvent(pHideEvent);
 }
 
 
 // Param kbob (widget) map accesors.
-void drumkv1widget::setParamKnob ( drumkv1::ParamIndex index, drumkv1widget_knob *pKnob )
+void drumkv1widget::setParamKnob ( drumkv1::ParamIndex index, drumkv1widget_param *pParam )
 {
-	pKnob->setDefaultValue(drumkv1_param::paramDefaultValue(index));
+	pParam->setDefaultValue(drumkv1_param::paramDefaultValue(index));
 
-	m_paramKnobs.insert(index, pKnob);
-	m_knobParams.insert(pKnob, index);
+	m_paramKnobs.insert(index, pParam);
+	m_knobParams.insert(pParam, index);
 
-	QObject::connect(pKnob,
+	QObject::connect(pParam,
 		SIGNAL(valueChanged(float)),
 		SLOT(paramChanged(float)));
 
-	pKnob->setContextMenuPolicy(Qt::CustomContextMenu);
+	pParam->setContextMenuPolicy(Qt::CustomContextMenu);
 
-	QObject::connect(pKnob,
+	QObject::connect(pParam,
 		SIGNAL(customContextMenuRequested(const QPoint&)),
 		SLOT(paramContextMenu(const QPoint&)));
 }
 
-drumkv1widget_knob *drumkv1widget::paramKnob ( drumkv1::ParamIndex index ) const
+drumkv1widget_param *drumkv1widget::paramKnob ( drumkv1::ParamIndex index ) const
 {
 	return m_paramKnobs.value(index, NULL);
 }
@@ -560,9 +600,9 @@ void drumkv1widget::setParamValue (
 {
 	++m_iUpdate;
 
-	drumkv1widget_knob *pKnob = paramKnob(index);
-	if (pKnob)
-		pKnob->setValue(fValue, bDefault);
+	drumkv1widget_param *pParam = paramKnob(index);
+	if (pParam)
+		pParam->setValue(fValue, bDefault);
 
 	updateParamEx(index, fValue);
 
@@ -573,9 +613,9 @@ float drumkv1widget::paramValue ( drumkv1::ParamIndex index ) const
 {
 	float fValue = 0.0f;
 
-	drumkv1widget_knob *pKnob = paramKnob(index);
-	if (pKnob) {
-		fValue = pKnob->value();
+	drumkv1widget_param *pParam = paramKnob(index);
+	if (pParam) {
+		fValue = pParam->value();
 	} else {
 		drumkv1_ui *pDrumkUi = ui_instance();
 		if (pDrumkUi)
@@ -592,9 +632,9 @@ void drumkv1widget::paramChanged ( float fValue )
 	if (m_iUpdate > 0)
 		return;
 
-	drumkv1widget_knob *pKnob = qobject_cast<drumkv1widget_knob *> (sender());
-	if (pKnob) {
-		const drumkv1::ParamIndex index = m_knobParams.value(pKnob);
+	drumkv1widget_param *pParam = qobject_cast<drumkv1widget_param *> (sender());
+	if (pParam) {
+		const drumkv1::ParamIndex index = m_knobParams.value(pParam);
 		// Save current element param value...
 		drumkv1_ui *pDrumkUi = ui_instance();
 		if (pDrumkUi) {
@@ -607,8 +647,8 @@ void drumkv1widget::paramChanged ( float fValue )
 		updateParam(index, fValue);
 		updateParamEx(index, fValue);
 		m_ui.StatusBar->showMessage(QString("%1: %2")
-			.arg(pKnob->toolTip())
-			.arg(pKnob->valueText()), 5000);
+			.arg(pParam->toolTip())
+			.arg(pParam->valueText()), 5000);
 		updateDirtyPreset(true);
 	}
 }
@@ -647,14 +687,14 @@ void drumkv1widget::updateSchedParam ( drumkv1::ParamIndex index, float fValue )
 {
 	++m_iUpdate;
 
-	drumkv1widget_knob *pKnob = paramKnob(index);
-	if (pKnob) {
-		pKnob->setValue(fValue, false);
+	drumkv1widget_param *pParam = paramKnob(index);
+	if (pParam) {
+		pParam->setValue(fValue, false);
 		updateParam(index, fValue);
 		updateParamEx(index, fValue);
 		m_ui.StatusBar->showMessage(QString("%1: %2")
-			.arg(pKnob->toolTip())
-			.arg(pKnob->valueText()), 5000);
+			.arg(pParam->toolTip())
+			.arg(pParam->valueText()), 5000);
 		updateDirtyPreset(true);
 	}
 
@@ -678,9 +718,9 @@ void drumkv1widget::resetParams (void)
 		if (index == drumkv1::GEN1_SAMPLE)
 			continue;
 		float fValue = drumkv1_param::paramDefaultValue(index);
-		drumkv1widget_knob *pKnob = paramKnob(index);
-		if (pKnob)
-			fValue = pKnob->defaultValue();
+		drumkv1widget_param *pParam = paramKnob(index);
+		if (pParam && pParam->isDefaultValue())
+			fValue = pParam->defaultValue();
 		setParamValue(index, fValue);
 		updateParam(index, fValue);
 	//	updateParamEx(index, fValue);
@@ -713,10 +753,10 @@ void drumkv1widget::swapParams ( bool bOn )
 				const drumkv1::ParamIndex index = drumkv1::ParamIndex(i);
 				if (index == drumkv1::GEN1_SAMPLE)
 					continue;
-				drumkv1widget_knob *pKnob = paramKnob(index);
-				if (pKnob) {
-					pKnob->setDefaultValue(element->paramValue(index, 0));
-					element->setParamValue(index, pKnob->value());
+				drumkv1widget_param *pParam = paramKnob(index);
+				if (pParam) {
+					pParam->setDefaultValue(element->paramValue(index, 0));
+					element->setParamValue(index, pParam->value());
 				}
 			}
 		}
@@ -737,9 +777,9 @@ void drumkv1widget::swapParams ( bool bOn )
 		const drumkv1::ParamIndex index = drumkv1::ParamIndex(i);
 		if (index == drumkv1::GEN1_SAMPLE)
 			continue;
-		drumkv1widget_knob *pKnob = paramKnob(index);
-		if (pKnob) {
-			const float fOldValue = pKnob->value();
+		drumkv1widget_param *pParam = paramKnob(index);
+		if (pParam) {
+			const float fOldValue = pParam->value();
 			const float fNewValue = m_params_ab[i];
 			setParamValue(index, fNewValue);
 			updateParam(index, fNewValue);
@@ -809,9 +849,9 @@ void drumkv1widget::resetParamKnobs ( uint32_t nparams )
 		const drumkv1::ParamIndex index = drumkv1::ParamIndex(i);
 		if (index == drumkv1::GEN1_SAMPLE)
 			continue;
-		drumkv1widget_knob *pKnob = paramKnob(index);
-		if (pKnob)
-			pKnob->resetDefaultValue();
+		drumkv1widget_param *pParam = paramKnob(index);
+		if (pParam)
+			pParam->resetDefaultValue();
 	}
 }
 
@@ -1068,13 +1108,17 @@ QString drumkv1widget::noteName ( int note )
 
 	// Pre-load drum-names hash table...
 	if (s_names.isEmpty()) {
-		for (int i = 12; s_notes[i].name; ++i) {
-			s_names.insert(s_notes[i].note,
-				QObject::tr(s_notes[i].name, "noteName"));
+		drumkv1_config *pConfig = drumkv1_config::getInstance();
+		if (pConfig && pConfig->bUseGMDrumNames) {
+			for (int i = 12; s_notes[i].name; ++i) {
+				s_names.insert(s_notes[i].note,
+					QObject::tr(s_notes[i].name, "noteName"));
+			}
 		}
 	}
+
 	// Check whether the drum note exists...
-	QHash<int, QString>::ConstIterator iter = s_names.constFind(note);
+	const QHash<int, QString>::ConstIterator& iter = s_names.constFind(note);
 	if (iter != s_names.constEnd())
 		return iter.value();
 
@@ -1199,10 +1243,10 @@ void drumkv1widget::updateElement (void)
 		for (uint32_t i = 0; i < drumkv1::NUM_ELEMENT_PARAMS; ++i) {
 			const drumkv1::ParamIndex index = drumkv1::ParamIndex(i);
 			const float fValue = element->paramValue(index);
-			drumkv1widget_knob *pKnob = paramKnob(index);
-			if (pKnob) {
-				pKnob->setDefaultValue(element->paramValue(index, 0));
-				pKnob->setValue(fValue);
+			drumkv1widget_param *pParam = paramKnob(index);
+			if (pParam) {
+				pParam->setDefaultValue(element->paramValue(index, 0));
+				pParam->setValue(fValue);
 			}
 			updateParam(index, fValue);
 			m_params_ab[i] = fValue;
@@ -1326,10 +1370,19 @@ void drumkv1widget::updateSchedNotify ( int stype, int sid )
 		return;
 
 #ifdef CONFIG_DEBUG
-	qDebug("drumkv1widget::updateSchedNotify(%d, %d)", stype, sid);
+	qDebug("drumkv1widget::updateSchedNotify(%d, 0x%04x)", stype, sid);
 #endif
 
 	switch (drumkv1_sched::Type(stype)) {
+	case drumkv1_sched::MidiIn:
+		if (sid >= 0)
+			m_ui.Elements->midiInLedNote(sid & 0x7f, (sid >> 7) & 0x7f);
+		else
+		if (pDrumkUi->midiInCount() > 0) {
+			m_ui.StatusBar->midiInLed(true);
+			QTimer::singleShot(200, this, SLOT(midiInLedTimeout()));
+		}
+		break;
 	case drumkv1_sched::Controller: {
 		drumkv1widget_control *pInstance
 			= drumkv1widget_control::getInstance();
@@ -1366,6 +1419,13 @@ void drumkv1widget::updateSchedNotify ( int stype, int sid )
 }
 
 
+// MIDI In LED timeout.
+void drumkv1widget::midiInLedTimeout (void)
+{
+	m_ui.StatusBar->midiInLed(false);
+}
+
+
 // Menu actions.
 void drumkv1widget::helpConfigure (void)
 {
@@ -1410,12 +1470,11 @@ void drumkv1widget::helpAbout (void)
 	sText += "<b>" DRUMKV1_TITLE "</b> - " + tr(DRUMKV1_SUBTITLE) + "<br />\n";
 	sText += "<br />\n";
 	sText += tr("Version") + ": <b>" CONFIG_BUILD_VERSION "</b><br />\n";
-	sText += "<small>" + tr("Build") + ": " CONFIG_BUILD_DATE "</small><br />\n";
-	QStringListIterator iter(list);
-	while (iter.hasNext()) {
+//	sText += "<small>" + tr("Build") + ": " CONFIG_BUILD_DATE "</small><br />\n";
+	if (!list.isEmpty()) {
 		sText += "<small><font color=\"red\">";
-		sText += iter.next();
-		sText += "</font></small><br />";
+		sText += list.join("<br />\n");
+		sText += "</font></small><br />\n";
 	}
 	sText += "<br />\n";
 	sText += tr("Website") + ": <a href=\"" DRUMKV1_WEBSITE "\">" DRUMKV1_WEBSITE "</a><br />\n";
@@ -1446,7 +1505,7 @@ void drumkv1widget::updateDirtyPreset ( bool bDirtyPreset )
 	if (pDrumkUi)
 		pDrumkUi->updatePreset(bDirtyPreset);
 
-	m_ui.StatusBar->setModified(bDirtyPreset);
+	m_ui.StatusBar->modified(bDirtyPreset);
 	m_ui.Preset->setDirtyPreset(bDirtyPreset);
 }
 
@@ -1454,9 +1513,9 @@ void drumkv1widget::updateDirtyPreset ( bool bDirtyPreset )
 // Param knob context menu.
 void drumkv1widget::paramContextMenu ( const QPoint& pos )
 {
-	drumkv1widget_knob *pKnob
-		= qobject_cast<drumkv1widget_knob *> (sender());
-	if (pKnob == NULL)
+	drumkv1widget_param *pParam
+		= qobject_cast<drumkv1widget_param *> (sender());
+	if (pParam == NULL)
 		return;
 
 	drumkv1_ui *pDrumkUi = ui_instance();
@@ -1476,9 +1535,9 @@ void drumkv1widget::paramContextMenu ( const QPoint& pos )
 		QIcon(":/images/drumkv1_control.png"),
 		tr("MIDI &Controller..."));
 
-	if (menu.exec(pKnob->mapToGlobal(pos)) == pAction) {
-		const drumkv1::ParamIndex index = m_knobParams.value(pKnob);
-		const QString& sTitle = pKnob->toolTip();
+	if (menu.exec(pParam->mapToGlobal(pos)) == pAction) {
+		const drumkv1::ParamIndex index = m_knobParams.value(pParam);
+		const QString& sTitle = pParam->toolTip();
 		drumkv1widget_control::showInstance(pControls, index, sTitle, this);
 	}
 }
diff --git a/src/drumkv1widget.h b/src/drumkv1widget.h
index 588e987..0abd927 100644
--- a/src/drumkv1widget.h
+++ b/src/drumkv1widget.h
@@ -1,7 +1,7 @@
 // drumkv1widget.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -50,8 +50,9 @@ public:
 	// Destructor.
 	virtual ~drumkv1widget();
 
-	// Create/initialize the scheduler/work notifier.
-	void initSchedNotifier();
+	// Open/close the scheduler/work notifier.
+	void openSchedNotifier();
+	void closeSchedNotifier();
 
 	// Param port accessors.
 	void setParamValue(
@@ -59,8 +60,8 @@ public:
 	float paramValue(drumkv1::ParamIndex index) const;
 
 	// Param kbob (widget) mapper.
-	void setParamKnob(drumkv1::ParamIndex index, drumkv1widget_knob *pKnob);
-	drumkv1widget_knob *paramKnob(drumkv1::ParamIndex index) const;
+	void setParamKnob(drumkv1::ParamIndex index, drumkv1widget_param *pKnob);
+	drumkv1widget_param *paramKnob(drumkv1::ParamIndex index) const;
 
 	// Preset init.
 	void initPreset();
@@ -128,6 +129,9 @@ protected slots:
 	// Notification updater.
 	void updateSchedNotify(int stype, int sid);
 
+	// MIDI In LED timeout.
+	void midiInLedTimeout();
+
 	// Param knob context menu.
 	void paramContextMenu(const QPoint& pos);
 
@@ -186,6 +190,10 @@ protected:
 	// Dirty flag (overridable virtual) methods.
 	virtual void updateDirtyPreset(bool bDirtyPreset);
 
+	// Show/hide dget handlers.
+	void showEvent(QShowEvent *pShowEvent);
+	void hideEvent(QHideEvent *pHideEvent);
+
 private:
 
 	// Instance variables.
@@ -193,8 +201,8 @@ private:
 
 	drumkv1widget_sched *m_sched_notifier;
 
-	QHash<drumkv1::ParamIndex, drumkv1widget_knob *> m_paramKnobs;
-	QHash<drumkv1widget_knob *, drumkv1::ParamIndex> m_knobParams;
+	QHash<drumkv1::ParamIndex, drumkv1widget_param *> m_paramKnobs;
+	QHash<drumkv1widget_param *, drumkv1::ParamIndex> m_knobParams;
 
 	float m_params_ab[drumkv1::NUM_PARAMS];
 
diff --git a/src/drumkv1widget.ui b/src/drumkv1widget.ui
index 08cd1e5..203861b 100644
--- a/src/drumkv1widget.ui
+++ b/src/drumkv1widget.ui
@@ -3,7 +3,7 @@
  <author>rncbc aka Rui Nuno Capela</author>
  <comment>drumkv1 - An old-school drum-kit sampler
 
-  Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+  Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
@@ -132,7 +132,7 @@
            </widget>
           </item>
           <item>
-           <widget class="drumkv1widget_combo" name="Gen1ReverseKnob">
+           <widget class="drumkv1widget_check" name="Gen1ReverseKnob">
             <property name="toolTip">
              <string>GEN Reverse</string>
             </property>
@@ -246,7 +246,7 @@
              </widget>
             </item>
             <item>
-             <widget class="drumkv1widget_combo" name="Dcf1TypeKnob">
+             <widget class="drumkv1widget_radio" name="Dcf1TypeKnob">
               <property name="toolTip">
                <string>DCF Type</string>
               </property>
@@ -256,7 +256,7 @@
              </widget>
             </item>
             <item>
-             <widget class="drumkv1widget_combo" name="Dcf1SlopeKnob">
+             <widget class="drumkv1widget_radio" name="Dcf1SlopeKnob">
               <property name="toolTip">
                <string>DCF Slope</string>
               </property>
@@ -347,7 +347,7 @@
              </widget>
             </item>
             <item>
-             <widget class="drumkv1widget_combo" name="Lfo1ShapeKnob">
+             <widget class="drumkv1widget_radio" name="Lfo1ShapeKnob">
               <property name="toolTip">
                <string>LFO Wave Shape</string>
               </property>
@@ -438,7 +438,7 @@
              </widget>
             </item>
             <item>
-             <widget class="drumkv1widget_combo" name="Lfo1SyncKnob">
+             <widget class="drumkv1widget_check" name="Lfo1SyncKnob">
               <property name="toolTip">
                <string>LFO Sync</string>
               </property>
@@ -674,7 +674,7 @@
            </widget>
           </item>
           <item>
-           <widget class="drumkv1widget_combo" name="Def1NoteoffKnob">
+           <widget class="drumkv1widget_check" name="Def1NoteoffKnob">
             <property name="toolTip">
              <string>DEF Note Off</string>
             </property>
@@ -1049,48 +1049,6 @@
          </layout>
         </widget>
        </item>
-       <item row="0" column="10" rowspan="2">
-        <widget class="QGroupBox" name="Dyn1GroupBox">
-         <property name="title">
-          <string>Dynamic</string>
-         </property>
-         <layout class="QVBoxLayout">
-          <item>
-           <widget class="drumkv1widget_combo" name="Dyn1CompressKnob">
-            <property name="toolTip">
-             <string>Dynamic Compressor</string>
-            </property>
-            <property name="text">
-             <string>Compress</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <spacer>
-            <property name="orientation">
-             <enum>Qt::Vertical</enum>
-            </property>
-            <property name="sizeHint">
-             <size>
-              <width>8</width>
-              <height>8</height>
-             </size>
-            </property>
-           </spacer>
-          </item>
-          <item>
-           <widget class="drumkv1widget_combo" name="Dyn1LimiterKnob">
-             <property name="toolTip">
-             <string>Dynamic Limiter</string>
-            </property>
-            <property name="text">
-             <string>Limiter</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </widget>
-       </item>
        <item row="0" column="11" rowspan="2">
         <spacer>
          <property name="orientation">
@@ -1176,6 +1134,35 @@
          </layout>
         </widget>
        </item>
+       <item row="2" column="6" colspan="4">
+        <widget class="QGroupBox" name="Dyn1GroupBox">
+         <property name="title">
+          <string>Dynamic</string>
+         </property>
+         <layout class="QHBoxLayout">
+          <item>
+           <widget class="drumkv1widget_check" name="Dyn1CompressKnob">
+            <property name="toolTip">
+             <string>Dynamic Compressor</string>
+            </property>
+            <property name="text">
+             <string>Compress</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="drumkv1widget_check" name="Dyn1LimiterKnob">
+             <property name="toolTip">
+             <string>Dynamic Limiter</string>
+            </property>
+            <property name="text">
+             <string>Limiter</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
        <item row="3" column="0" colspan="12">
         <spacer>
          <property name="orientation">
@@ -1246,12 +1233,22 @@
   <customwidget>
    <class>drumkv1widget_spin</class>
    <extends>QDial</extends>
-   <header>drumkv1widget_knob.h</header>
+   <header>drumkv1widget_param.h</header>
   </customwidget>
   <customwidget>
    <class>drumkv1widget_combo</class>
    <extends>QDial</extends>
-   <header>drumkv1widget_knob.h</header>
+   <header>drumkv1widget_param.h</header>
+  </customwidget>
+  <customwidget>
+   <class>drumkv1widget_radio</class>
+   <extends>QDial</extends>
+   <header>drumkv1widget_param.h</header>
+  </customwidget>
+  <customwidget>
+   <class>drumkv1widget_check</class>
+   <extends>QCheckBox</extends>
+   <header>drumkv1widget_param.h</header>
   </customwidget>
   <customwidget>
    <class>drumkv1widget_env</class>
diff --git a/src/drumkv1widget_config.cpp b/src/drumkv1widget_config.cpp
index 7f46ff2..cf1cca7 100644
--- a/src/drumkv1widget_config.cpp
+++ b/src/drumkv1widget_config.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_config.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -20,7 +20,7 @@
 *****************************************************************************/
 
 #include "drumkv1widget_config.h"
-#include "drumkv1widget_knob.h"
+#include "drumkv1widget_param.h"
 
 #include <QPushButton>
 #include <QMessageBox>
@@ -52,11 +52,13 @@ drumkv1widget_config::drumkv1widget_config (
 		m_ui.ProgramsPreviewCheckBox->setChecked(pConfig->bProgramsPreview);
 		m_ui.UseNativeDialogsCheckBox->setChecked(pConfig->bUseNativeDialogs);
 		m_ui.KnobDialModeComboBox->setCurrentIndex(pConfig->iKnobDialMode);
+		m_ui.KnobEditModeComboBox->setCurrentIndex(pConfig->iKnobEditMode);
 		int iCustomStyleTheme = 0;
 		if (!pConfig->sCustomStyleTheme.isEmpty())
 			iCustomStyleTheme = m_ui.CustomStyleThemeComboBox->findText(
 				pConfig->sCustomStyleTheme);
 		m_ui.CustomStyleThemeComboBox->setCurrentIndex(iCustomStyleTheme);
+		m_ui.UseGMDrumNamesCheckBox->setChecked(pConfig->bUseGMDrumNames);
 	}
 
 	// Signal/slots connections...
@@ -129,9 +131,15 @@ drumkv1widget_config::drumkv1widget_config (
 	QObject::connect(m_ui.KnobDialModeComboBox,
 		SIGNAL(activated(int)),
 		SLOT(optionsChanged()));
+	QObject::connect(m_ui.KnobEditModeComboBox,
+		SIGNAL(activated(int)),
+		SLOT(optionsChanged()));
 	QObject::connect(m_ui.CustomStyleThemeComboBox,
 		SIGNAL(activated(int)),
 		SLOT(optionsChanged()));
+	QObject::connect(m_ui.UseGMDrumNamesCheckBox,
+		SIGNAL(toggled(bool)),
+		SLOT(optionsChanged()));
 
 	// Dialog commands...
 	QObject::connect(m_ui.DialogButtonBox,
@@ -469,13 +477,19 @@ void drumkv1widget_config::accept (void)
 		pConfig->iKnobDialMode = m_ui.KnobDialModeComboBox->currentIndex();
 		drumkv1widget_dial::setDialMode(
 			drumkv1widget_dial::DialMode(pConfig->iKnobDialMode));
+		pConfig->iKnobEditMode = m_ui.KnobEditModeComboBox->currentIndex();
+		drumkv1widget_edit::setEditMode(
+			drumkv1widget_edit::EditMode(pConfig->iKnobEditMode));
 		const QString sOldCustomStyleTheme = pConfig->sCustomStyleTheme;
 		if (m_ui.CustomStyleThemeComboBox->currentIndex() > 0)
 			pConfig->sCustomStyleTheme = m_ui.CustomStyleThemeComboBox->currentText();
 		else
 			pConfig->sCustomStyleTheme.clear();
+		const bool bOldUseGMDrumNames = pConfig->bUseGMDrumNames;
+		pConfig->bUseGMDrumNames = m_ui.UseGMDrumNamesCheckBox->isChecked();
 		// Show restart needed message...
-		if (pConfig->sCustomStyleTheme != sOldCustomStyleTheme) {
+		if ((pConfig->sCustomStyleTheme != sOldCustomStyleTheme) ||
+			(!pConfig->bUseGMDrumNames && bOldUseGMDrumNames)) {
 			QMessageBox::information(this,
 				tr("Information") + " - " DRUMKV1_TITLE,
 				tr("Some settings may be only effective\n"
diff --git a/src/drumkv1widget_config.h b/src/drumkv1widget_config.h
index 2306268..4fe4fba 100644
--- a/src/drumkv1widget_config.h
+++ b/src/drumkv1widget_config.h
@@ -1,7 +1,7 @@
 // drumkv1widget_config.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1widget_config.ui b/src/drumkv1widget_config.ui
index 437ac7e..80b35f2 100644
--- a/src/drumkv1widget_config.ui
+++ b/src/drumkv1widget_config.ui
@@ -3,7 +3,7 @@
  <author>rncbc aka Rui Nuno Capela</author>
  <comment>drumkv1 - An old-school drum-kit sampler
 
-  Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+  Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
@@ -95,7 +95,7 @@
          </item>
         </widget>
        </item>
-       <item row="1" column="2" colspan="2">
+       <item row="1" column="2" colspan="2" rowspan="3">
         <spacer>
          <property name="orientation">
           <enum>Qt::Horizontal</enum>
@@ -108,7 +108,37 @@
          </property>
         </spacer>
        </item>
-	   <item row="2" column="0">
+       <item row="2" column="0">
+        <widget class="QLabel" name="KnobEditModeTextLabel">
+         <property name="text">
+          <string>Knob &edit mode:</string>
+         </property>
+         <property name="buddy">
+          <cstring>KnobEditModeComboBox</cstring>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QComboBox" name="KnobEditModeComboBox">
+         <property name="toolTip">
+          <string>Knob edit mode</string>
+         </property>
+         <property name="editable">
+          <bool>false</bool>
+         </property>
+         <item>
+          <property name="text">
+           <string>(default)</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Deferred</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="3" column="0">
         <widget class="QLabel" name="CustomStyleThemeTextLabel">
          <property name="text">
           <string>Custom &style theme:</string>
@@ -118,7 +148,7 @@
          </property>
         </widget>
        </item>
-       <item row="2" column="1">
+       <item row="3" column="1">
         <widget class="QComboBox" name="CustomStyleThemeComboBox">
          <property name="toolTip">
           <string>Custom widget style theme</string>
@@ -133,7 +163,7 @@
          </item>
         </widget>
        </item>
-       <item row="3" colspan="3">
+       <item row="4" colspan="3">
         <spacer>
          <property name="orientation">
           <enum>Qt::Vertical</enum>
@@ -146,6 +176,16 @@
          </property>
         </spacer>
        </item>
+       <item row="4" column="0" colspan="3">
+        <widget class="QCheckBox" name="UseGMDrumNamesCheckBox">
+         <property name="toolTip">
+          <string>Whether to use GM Standard drum names</string>
+         </property>
+         <property name="text">
+          <string>Use &GM Standard drum names</string>
+         </property>
+        </widget>
+       </item>
       </layout>
      </widget>
      <widget class="QWidget" name="ProgramsTab">
@@ -499,7 +539,9 @@
  <tabstops>
   <tabstop>UseNativeDialogsCheckBox</tabstop>
   <tabstop>KnobDialModeComboBox</tabstop>
+  <tabstop>KnobEditModeComboBox</tabstop>
   <tabstop>CustomStyleThemeComboBox</tabstop>
+  <tabstop>UseGMDrumNamesCheckBox</tabstop>
   <tabstop>ProgramsAddBankToolButton</tabstop>
   <tabstop>ProgramsAddItemToolButton</tabstop>
   <tabstop>ProgramsEditToolButton</tabstop>
diff --git a/src/drumkv1widget_elements.cpp b/src/drumkv1widget_elements.cpp
index 4bcc49a..a983b0e 100644
--- a/src/drumkv1widget_elements.cpp
+++ b/src/drumkv1widget_elements.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_elements.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -27,12 +27,14 @@
 
 #include "drumkv1_sample.h"
 
-#include <QAbstractItemModel>
 #include <QHeaderView>
 #include <QFileInfo>
 #include <QMimeData>
 #include <QDrag>
 #include <QUrl>
+#include <QIcon>
+#include <QPixmap>
+#include <QTimer>
 
 #include <QDragEnterEvent>
 #include <QDragMoveEvent>
@@ -42,66 +44,46 @@
 //----------------------------------------------------------------------------
 // drumkv1widget_elements_model -- List model.
 
-class drumkv1widget_elements_model : public QAbstractItemModel
-{
-public:
-
-	// Constructor.
-	drumkv1widget_elements_model(drumkv1_ui *pDrumkUi, QObject *pParent = NULL);
-
-	// Concretizers (virtual).
-	int rowCount(const QModelIndex& parent = QModelIndex()) const;
-	int columnCount(const QModelIndex& parent = QModelIndex()) const;
-
-	QVariant headerData(int section, Qt::Orientation orient, int role) const;
-	QVariant data(const QModelIndex& index, int role) const;
-
-	QModelIndex index(int row, int column,
-		const QModelIndex& parent = QModelIndex()) const;
-
-	QModelIndex parent(const QModelIndex&) const;
-
-	void reset();
-
-	// Accessor specific.
-	drumkv1_ui *instance() const;
-
-protected:
-
-	// Other specifics
-	drumkv1_element *elementFromIndex(const QModelIndex& index) const;
-
-	QString itemDisplay(const QModelIndex& index) const;
-	QString itemToolTip(const QModelIndex& index) const;
-
-	int columnAlignment(int column) const;
-
-private:
-
-	// Model variables.
-	QStringList m_headers;
-
-	drumkv1_ui *m_pDrumkUi;
-};
-
-
 // Constructor.
 drumkv1widget_elements_model::drumkv1widget_elements_model (
 	drumkv1_ui *pDrumkUi, QObject *pParent )
 	: QAbstractItemModel(pParent), m_pDrumkUi(pDrumkUi)
 {
+	QIcon icon;
+
+	icon.addPixmap(
+		QPixmap(":/images/ledOff.png"), QIcon::Normal, QIcon::Off);
+	icon.addPixmap(
+		QPixmap(":/images/ledOn.png"), QIcon::Normal, QIcon::On);
+
+	m_pixmaps[0] = new QPixmap(
+		icon.pixmap(12, 12, QIcon::Normal, QIcon::Off));
+	m_pixmaps[1] = new QPixmap(
+		icon.pixmap(12, 12, QIcon::Normal, QIcon::On));
+
 	m_headers
 		<< tr("Element")
 		<< tr("Sample");
 
+	for (int i = 0; i < MAX_NOTES; ++i)
+		m_notes_on[i] = 0;
+
 	reset();
 }
 
 
+// Destructor.
+drumkv1widget_elements_model::~drumkv1widget_elements_model (void)
+{
+	delete m_pixmaps[1];
+	delete m_pixmaps[0];
+}
+
+
 int drumkv1widget_elements_model::rowCount (
 	const QModelIndex& /*parent*/ ) const
 {
-	return 128;
+	return MAX_NOTES;
 }
 
 
@@ -133,6 +115,10 @@ QVariant drumkv1widget_elements_model::data (
 	const QModelIndex& index, int role ) const
 {
 	switch (role) {
+	case Qt::DecorationRole:
+		if (index.column() == 0)
+			return *m_pixmaps[m_notes_on[index.row()] > 0 ? 1 : 0];
+		break;
 	case Qt::DisplayRole:
 		return itemDisplay(index);
 	case Qt::TextAlignmentRole:
@@ -172,6 +158,44 @@ drumkv1_ui *drumkv1widget_elements_model::instance (void) const
 }
 
 
+void drumkv1widget_elements_model::midiInLedNote ( int key, int vel )
+{
+	if (vel > 0) {
+		m_notes_on[key] = vel;
+		midiInLedUpdate(key);
+	}
+	else
+	if (m_notes_on[key] > 0) {
+		m_notes_off.append(key);
+		QTimer::singleShot(200, this, SLOT(midiInLedTimeout()));
+	}
+}
+
+
+void drumkv1widget_elements_model::midiInLedTimeout (void)
+{
+	QListIterator<int> iter(m_notes_off);
+	while (iter.hasNext()) {
+		const int key = iter.next();
+		midiInLedUpdate(key);
+		m_notes_on[key] = 0;
+	}
+
+	m_notes_off.clear();
+}
+
+
+void drumkv1widget_elements_model::midiInLedUpdate ( int key )
+{
+	const QModelIndex& index = drumkv1widget_elements_model::index(key, 0);
+#if QT_VERSION >= 0x050100
+	emit dataChanged(index, index, QVector<int>() << Qt::DecorationRole);
+#else
+	emit dataChanged(index, index);
+#endif
+}
+
+
 void drumkv1widget_elements_model::reset (void)
 {
 #if QT_VERSION < 0x050000
@@ -186,7 +210,6 @@ void drumkv1widget_elements_model::reset (void)
 QString drumkv1widget_elements_model::itemDisplay (
 	const QModelIndex& index ) const
 {
-	const QString sDash('-');
 	switch (index.column()) {
 	case 0: // Element.
 		return drumkv1widget::completeNoteName(index.row());
@@ -200,7 +223,7 @@ QString drumkv1widget_elements_model::itemDisplay (
 				return tr("(None)");
 		}
 	}
-	return sDash;
+	return QString('-');
 }
 
 
@@ -271,12 +294,7 @@ void drumkv1widget_elements::setInstance ( drumkv1_ui *pDrumkUi )
 	QTreeView::setAcceptDrops(true);
 
 	QHeaderView *pHeader = QTreeView::header();
-	//	pHeader->setDefaultAlignment(Qt::AlignLeft);
-#if QT_VERSION >= 0x050000
-	pHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
-#else
-	pHeader->setResizeMode(QHeaderView::ResizeToContents);
-#endif
+	pHeader->setDefaultAlignment(Qt::AlignLeft);
 	pHeader->setStretchLastSection(true);
 
 	// Element selectors
@@ -433,6 +451,8 @@ void drumkv1widget_elements::refresh (void)
 
 	m_pModel->reset();
 
+	QTreeView::header()->resizeSections(QHeaderView::ResizeToContents);
+
 	pSelectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
 }
 
@@ -444,4 +464,13 @@ QSize drumkv1widget_elements::sizeHint (void) const
 }
 
 
+// MIDI input status update
+void drumkv1widget_elements::midiInLedNote ( int key, int vel )
+{
+	if (m_pModel)
+		m_pModel->midiInLedNote(key, vel);
+}
+
+
+
 // end of drumkv1widget_elements.cpp
diff --git a/src/drumkv1widget_elements.h b/src/drumkv1widget_elements.h
index 159e354..433a4a8 100644
--- a/src/drumkv1widget_elements.h
+++ b/src/drumkv1widget_elements.h
@@ -1,7 +1,7 @@
 // drumkv1widget_elements.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -23,21 +23,87 @@
 #define __drumkv1widget_elements_h
 
 #include <QTreeView>
+#include <QAbstractItemModel>
 
 
 // Forwards.
-class drumkv1widget_elements_model;
-
 class drumkv1_ui;
 
+class drumkv1_element;
 class drumkv1_sample;
 
+class QPixmap;
+
 class QDragEnterEvent;
 class QDragMoveEvent;
 class QDropEvent;
 
 
 //----------------------------------------------------------------------------
+// drumkv1widget_elements_model -- List model.
+
+class drumkv1widget_elements_model : public QAbstractItemModel
+{
+	Q_OBJECT
+
+public:
+
+	// Constructor.
+	drumkv1widget_elements_model(drumkv1_ui *pDrumkUi, QObject *pParent = NULL);
+
+	// Destructor.
+	~drumkv1widget_elements_model();
+
+	// Concretizers (virtual).
+	int rowCount(const QModelIndex& parent = QModelIndex()) const;
+	int columnCount(const QModelIndex& parent = QModelIndex()) const;
+
+	QVariant headerData(int section, Qt::Orientation orient, int role) const;
+	QVariant data(const QModelIndex& index, int role) const;
+
+	QModelIndex index(int row, int column,
+		const QModelIndex& parent = QModelIndex()) const;
+
+	QModelIndex parent(const QModelIndex&) const;
+
+	void reset();
+
+	// Accessor specific.
+	drumkv1_ui *instance() const;
+
+	void midiInLedNote(int key, int vel);
+
+protected slots:
+
+	void midiInLedTimeout();
+
+protected:
+
+	// Other specifics
+	drumkv1_element *elementFromIndex(const QModelIndex& index) const;
+
+	QString itemDisplay(const QModelIndex& index) const;
+	QString itemToolTip(const QModelIndex& index) const;
+
+	int columnAlignment(int column) const;
+
+	void midiInLedUpdate(int key);
+
+private:
+
+	// Model variables.
+	QPixmap    *m_pixmaps[2];
+	QStringList m_headers;
+
+	drumkv1_ui *m_pDrumkUi;
+
+	static const int MAX_NOTES = 128;
+	int m_notes_on[MAX_NOTES];
+	QList<int> m_notes_off;
+};
+
+
+//----------------------------------------------------------------------------
 // drumkv1widget_elements -- Custom (tree) list view.
 
 class drumkv1widget_elements : public QTreeView
@@ -63,6 +129,9 @@ public:
 	// Refreshener.
 	void refresh();
 
+	// MIDI input status update
+	void midiInLedNote(int key, int vel);
+
 signals:
 
 	// Emitted signals.
diff --git a/src/drumkv1widget_env.cpp b/src/drumkv1widget_env.cpp
index 2d325b9..a7190fa 100644
--- a/src/drumkv1widget_env.cpp
+++ b/src/drumkv1widget_env.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_env.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -45,7 +45,7 @@ drumkv1widget_env::drumkv1widget_env (
 		m_poly(6), m_iDragNode(-1)
 {
 	setMouseTracking(true);
-	setMinimumSize(QSize(120, 60));
+	setMinimumSize(QSize(120, 72));
 
 	QFrame::setFrameShape(QFrame::Panel);
 	QFrame::setFrameShadow(QFrame::Sunken);
diff --git a/src/drumkv1widget_env.h b/src/drumkv1widget_env.h
index 0d30f4b..866c5a0 100644
--- a/src/drumkv1widget_env.h
+++ b/src/drumkv1widget_env.h
@@ -1,7 +1,7 @@
 // drumkv1widget_env.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1widget_filt.cpp b/src/drumkv1widget_filt.cpp
index 0622c32..ed41056 100644
--- a/src/drumkv1widget_filt.cpp
+++ b/src/drumkv1widget_filt.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_filt.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -46,7 +46,7 @@ drumkv1widget_filt::drumkv1widget_filt (
 		m_bDragging(false)
 {
 //	setMouseTracking(true);
-	setMinimumSize(QSize(180, 60));
+	setMinimumSize(QSize(180, 72));
 
 	QFrame::setFrameShape(QFrame::Panel);
 	QFrame::setFrameShadow(QFrame::Sunken);
diff --git a/src/drumkv1widget_filt.h b/src/drumkv1widget_filt.h
index 9254ec8..4c59d51 100644
--- a/src/drumkv1widget_filt.h
+++ b/src/drumkv1widget_filt.h
@@ -1,7 +1,7 @@
 // drumkv1widget_filt.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1widget_jack.cpp b/src/drumkv1widget_jack.cpp
index 877e007..079d926 100644
--- a/src/drumkv1widget_jack.cpp
+++ b/src/drumkv1widget_jack.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_jack.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -73,7 +73,7 @@ drumkv1widget_jack::drumkv1widget_jack ( drumkv1_jack *pDrumk )
 	m_pDrumkUi = new drumkv1_ui(m_pDrumk);
 
 	// May initialize the scheduler/work notifier.
-	initSchedNotifier();
+	openSchedNotifier();
 
 	// Initialize preset stuff...
 	//initPreset();
diff --git a/src/drumkv1widget_jack.h b/src/drumkv1widget_jack.h
index 756f000..e3a06e6 100644
--- a/src/drumkv1widget_jack.h
+++ b/src/drumkv1widget_jack.h
@@ -1,7 +1,7 @@
 // drumkv1widget_jack.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1widget_knob.cpp b/src/drumkv1widget_knob.cpp
deleted file mode 100644
index 2d8400c..0000000
--- a/src/drumkv1widget_knob.cpp
+++ /dev/null
@@ -1,552 +0,0 @@
-// drumkv1widget_knob.cpp
-//
-/****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
-
-   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 "drumkv1widget_knob.h"
-
-#include <QLabel>
-#include <QSpinBox>
-#include <QComboBox>
-
-#include <QGridLayout>
-
-#include <QMouseEvent>
-
-#include <math.h>
-
-
-// Integer value round.
-inline int iroundf(float x) { return int(x < 0.0f ? x - 0.5f : x + 0.5f); }
-
-
-//-------------------------------------------------------------------------
-// drumkv1widget_dial - A better QDial widget.
-
-drumkv1widget_dial::DialMode
-drumkv1widget_dial::g_dialMode = drumkv1widget_dial::DefaultMode;
-
-// Set knob dial mode behavior.
-void drumkv1widget_dial::setDialMode ( DialMode dialMode )
-	{ g_dialMode = dialMode; }
-
-drumkv1widget_dial::DialMode drumkv1widget_dial::dialMode (void)
-	{ return g_dialMode; }
-
-
-// Constructor.
-drumkv1widget_dial::drumkv1widget_dial ( QWidget *pParent )
-	: QDial(pParent), m_bMousePressed(false), m_fLastDragValue(0.0f)
-{
-}
-
-
-// Mouse angle determination.
-float drumkv1widget_dial::mouseAngle ( const QPoint& pos )
-{
-	const float dx = pos.x() - (width() >> 1);
-	const float dy = (height() >> 1) - pos.y();
-	return 180.0f * ::atan2f(dx, dy) / float(M_PI);
-}
-
-
-// Alternate mouse behavior event handlers.
-void drumkv1widget_dial::mousePressEvent ( QMouseEvent *pMouseEvent )
-{
-	if (g_dialMode == DefaultMode) {
-		QDial::mousePressEvent(pMouseEvent);
-	} else if (pMouseEvent->button() == Qt::LeftButton) {
-		m_bMousePressed = true;
-		m_posMouse = pMouseEvent->pos();
-		m_fLastDragValue = float(value());
-		emit sliderPressed();
-	}
-}
-
-
-void drumkv1widget_dial::mouseMoveEvent ( QMouseEvent *pMouseEvent )
-{
-	if (g_dialMode == DefaultMode) {
-		QDial::mouseMoveEvent(pMouseEvent);
-		return;
-	}
-
-	if (!m_bMousePressed)
-		return;
-
-	const QPoint& pos = pMouseEvent->pos();
-	const int dx = pos.x() - m_posMouse.x();
-	const int dy = pos.y() - m_posMouse.y();
-	float fAngleDelta =  mouseAngle(pos) - mouseAngle(m_posMouse);
-	int iNewValue = value();
-
-	switch (g_dialMode)	{
-	case LinearMode:
-		iNewValue = int(m_fLastDragValue) + dx - dy;
-		break;
-	case AngularMode:
-	default:
-		// Forget about the drag origin to be robust on full rotations
-		if (fAngleDelta > +180.0f) fAngleDelta -= 360.0f;
-		else
-		if (fAngleDelta < -180.0f) fAngleDelta += 360.0f;
-		m_fLastDragValue += float(maximum() - minimum()) * fAngleDelta / 270.0f;
-		if (m_fLastDragValue > float(maximum()))
-			m_fLastDragValue = float(maximum());
-		else
-		if (m_fLastDragValue < float(minimum()))
-			m_fLastDragValue = float(minimum());
-		m_posMouse = pos;
-		iNewValue = int(m_fLastDragValue + 0.5f);
-		break;
-	}
-
-	setValue(iNewValue);
-	update();
-
-	emit sliderMoved(value());
-}
-
-
-void drumkv1widget_dial::mouseReleaseEvent ( QMouseEvent *pMouseEvent )
-{
-	if (g_dialMode == DefaultMode
-		&& pMouseEvent->button() != Qt::MidButton) {
-		QDial::mouseReleaseEvent(pMouseEvent);
-	} else if (m_bMousePressed) {
-		m_bMousePressed = false;
-	}
-}
-
-
-//-------------------------------------------------------------------------
-// drumkv1widget_knob - Custom composite widget.
-//
-
-// Constructor.
-drumkv1widget_knob::drumkv1widget_knob ( QWidget *pParent ) : QWidget(pParent)
-{
-	const QFont& font = QWidget::font();
-	const QFont font2(font.family(), font.pointSize() - 2);
-	QWidget::setFont(font2);
-
-	m_pLabel = new QLabel();
-	m_pDial  = new drumkv1widget_dial();
-
-	m_fScale = 100.0f;
-
-	resetDefaultValue();
-
-	m_pLabel->setAlignment(Qt::AlignCenter);
-	m_pDial->setNotchesVisible(true);
-	m_pDial->setMaximumSize(QSize(48, 42));
-
-	QGridLayout *pGridLayout = new QGridLayout();
-	pGridLayout->setMargin(0);
-	pGridLayout->setSpacing(0);
-	pGridLayout->addWidget(m_pLabel, 0, 0, 1, 3);
-	pGridLayout->addWidget(m_pDial,  1, 0, 1, 3);
-	QWidget::setLayout(pGridLayout);
-
-	QWidget::setMaximumSize(QSize(52, 72));
-
-	QObject::connect(m_pDial,
-		SIGNAL(valueChanged(int)),
-		SLOT(dialValueChanged(int)));
-}
-
-
-void drumkv1widget_knob::setText ( const QString& sText )
-{
-	m_pLabel->setText(sText);
-}
-
-
-QString drumkv1widget_knob::text (void) const
-{
-	return m_pLabel->text();
-}
-
-
-void drumkv1widget_knob::setValue ( float fValue, bool bDefault )
-{
-	const bool bDialBlock = m_pDial->blockSignals(true);
-
-	m_pDial->setValue(scaleFromValue(fValue));
-
-	QPalette pal;
-	if (m_iDefaultValue < 1 || bDefault) {
-		m_fDefaultValue = fValue;
-		m_iDefaultValue++;
-	}
-	else
-	if (QWidget::isEnabled()
-		&& ::fabsf(fValue - m_fDefaultValue) > 0.0001f) {
-		pal.setColor(QPalette::Base,
-			(pal.window().color().value() < 0x7f
-				? QColor(Qt::darkYellow).darker()
-				: QColor(Qt::yellow).lighter()));
-	}
-	QWidget::setPalette(pal);
-
-	emit valueChanged(fValue);
-
-	m_pDial->blockSignals(bDialBlock);
-}
-
-
-float drumkv1widget_knob::value (void) const
-{
-	return valueFromScale(m_pDial->value());
-}
-
-
-QString drumkv1widget_knob::valueText (void) const
-{
-	return QString::number(value());
-}
-
-
-void drumkv1widget_knob::setMaximum ( float fMaximum )
-{
-	m_pDial->setMaximum(scaleFromValue(fMaximum));
-}
-
-
-float drumkv1widget_knob::maximum (void) const
-{
-	return valueFromScale(m_pDial->maximum());
-}
-
-
-void drumkv1widget_knob::setMinimum ( float fMinimum )
-{
-	m_pDial->setMinimum(scaleFromValue(fMinimum));
-}
-
-
-float drumkv1widget_knob::minimum (void) const
-{
-	return valueFromScale(m_pDial->minimum());
-}
-
-
-void drumkv1widget_knob::resetDefaultValue (void)
-{
-	m_fDefaultValue = 0.0f;
-	m_iDefaultValue = 0;
-}
-
-
-void drumkv1widget_knob::setDefaultValue ( float fDefaultValue )
-{
-	m_fDefaultValue = fDefaultValue;
-	m_iDefaultValue++;
-}
-
-
-float drumkv1widget_knob::defaultValue (void) const
-{
-	return m_fDefaultValue;
-}
-
-
-void drumkv1widget_knob::setSingleStep ( float fSingleStep )
-{
-	m_pDial->setSingleStep(scaleFromValue(fSingleStep));
-}
-
-
-float drumkv1widget_knob::singleStep (void) const
-{
-	return valueFromScale(m_pDial->singleStep());
-}
-
-
-// Mouse behavior event handler.
-void drumkv1widget_knob::mousePressEvent ( QMouseEvent *pMouseEvent )
-{
-	if (pMouseEvent->button() == Qt::MidButton) {
-		if (m_iDefaultValue < 1) {
-			m_fDefaultValue = 0.5f * (maximum() + minimum());
-			m_iDefaultValue++;
-		}
-		setValue(m_fDefaultValue);
-	}
-
-	QWidget::mousePressEvent(pMouseEvent);
-}
-
-
-// Internal widget slots.
-void drumkv1widget_knob::dialValueChanged ( int iDialValue )
-{
-	setValue(valueFromScale(iDialValue));
-}
-
-
-
-// Scale multiplier (default=100).
-void drumkv1widget_knob::setScale ( float fScale )
-{
-	m_fScale = fScale;
-
-	m_pDial->setNotchTarget(valueFromScale(33.3f));
-}
-
-
-float drumkv1widget_knob::scale (void) const
-{
-	return m_fScale;
-}
-
-
-// Scale/value converters.
-float drumkv1widget_knob::scaleFromValue ( float fValue ) const
-{
-	return (m_fScale * fValue);
-}
-
-
-float drumkv1widget_knob::valueFromScale ( float fScale ) const
-{
-	return (fScale / m_fScale);
-}
-
-
-//-------------------------------------------------------------------------
-// drumkv1widget_spin - Custom knob/spin-box widget.
-//
-
-// Constructor.
-drumkv1widget_spin::drumkv1widget_spin ( QWidget *pParent )
-	: drumkv1widget_knob(pParent)
-{
-	m_pSpinBox = new QDoubleSpinBox();
-	m_pSpinBox->setAccelerated(true);
-	m_pSpinBox->setAlignment(Qt::AlignCenter);
-
-	const QFontMetrics fm(drumkv1widget_knob::font());
-	m_pSpinBox->setMaximumHeight(fm.height() + 6);
-
-	QGridLayout *pGridLayout
-		= static_cast<QGridLayout *> (QWidget::layout());
-	pGridLayout->addWidget(m_pSpinBox, 2, 1, 1, 1);
-
-	setMinimum(0.0f);
-	setMaximum(1.0f);
-
-	setDecimals(1);
-
-	QObject::connect(m_pSpinBox,
-		SIGNAL(valueChanged(double)),
-		SLOT(spinBoxValueChanged(double)));
-}
-
-
-// Virtual accessors.
-void drumkv1widget_spin::setValue ( float fValue, bool bDefault )
-{
-	const float fSpinValue = scaleFromValue(fValue);
-	if (::fabsf(fSpinValue - float(m_pSpinBox->value())) < 0.001f)
-		return;
-
-	const bool bSpinBlock = m_pSpinBox->blockSignals(true);
-	m_pSpinBox->setValue(fSpinValue);
-	drumkv1widget_knob::setValue(fValue, bDefault);
-	m_pSpinBox->blockSignals(bSpinBlock);
-}
-
-
-void drumkv1widget_spin::setMaximum ( float fMaximum )
-{
-	m_pSpinBox->setMaximum(scaleFromValue(fMaximum));
-	drumkv1widget_knob::setMaximum(fMaximum);
-}
-
-
-void drumkv1widget_spin::setMinimum ( float fMinimum )
-{
-	m_pSpinBox->setMinimum(scaleFromValue(fMinimum));
-	drumkv1widget_knob::setMinimum(fMinimum);
-}
-
-
-void drumkv1widget_spin::setSingleStep ( float fSingleStep )
-{
-	m_pSpinBox->setSingleStep(fSingleStep);
-	drumkv1widget_knob::setSingleStep(fSingleStep);
-}
-
-
-QString drumkv1widget_spin::valueText (void) const
-{
-	return QString::number(m_pSpinBox->value(), 'f', 1);
-}
-
-
-float drumkv1widget_spin::value (void) const
-{
-	return valueFromScale(m_pSpinBox->value());
-}
-
-
-// Internal widget slots.
-void drumkv1widget_spin::spinBoxValueChanged ( double spinValue )
-{
-	drumkv1widget_knob::setValue(valueFromScale(float(spinValue)));
-}
-
-
-// Special value text (minimum)
-void drumkv1widget_spin::setSpecialValueText ( const QString& sText )
-{
-	m_pSpinBox->setSpecialValueText(sText);
-}
-
-
-QString drumkv1widget_spin::specialValueText (void) const
-{
-	return m_pSpinBox->specialValueText();
-}
-
-
-bool drumkv1widget_spin::isSpecialValue (void) const
-{
-	return (m_pSpinBox->minimum() >= m_pSpinBox->value());
-}
-
-
-// Decimal digits allowed.
-void drumkv1widget_spin::setDecimals ( int iDecimals )
-{
-	m_pSpinBox->setDecimals(iDecimals);
-
-	setSingleStep(::powf(10.0f, - float(iDecimals)));
-}
-
-int drumkv1widget_spin::decimals (void) const
-{
-	return m_pSpinBox->decimals();
-}
-
-
-//-------------------------------------------------------------------------
-// drumkv1widget_combo - Custom knob/combo-box widget.
-//
-
-// Constructor.
-drumkv1widget_combo::drumkv1widget_combo ( QWidget *pParent )
-	: drumkv1widget_knob(pParent)
-{
-	m_pComboBox = new QComboBox();
-
-	const QFontMetrics fm(drumkv1widget_knob::font());
-	m_pComboBox->setMaximumHeight(fm.height() + 6);
-
-	QGridLayout *pGridLayout
-		= static_cast<QGridLayout *> (QWidget::layout());
-	pGridLayout->addWidget(m_pComboBox, 2, 0, 1, 3);
-
-	QObject::connect(m_pComboBox,
-		SIGNAL(activated(int)),
-		SLOT(comboBoxValueChanged(int)));
-}
-
-
-// Virtual accessors.
-void drumkv1widget_combo::setValue ( float fValue, bool bDefault )
-{
-	const int iComboValue = iroundf(fValue);
-	if (iComboValue == m_pComboBox->currentIndex())
-		return;
-
-	const bool bComboBlock = m_pComboBox->blockSignals(true);
-	m_pComboBox->setCurrentIndex(iComboValue);
-	drumkv1widget_knob::setValue(float(iComboValue), bDefault);
-	m_pComboBox->blockSignals(bComboBlock);
-}
-
-
-QString drumkv1widget_combo::valueText (void) const
-{
-	return m_pComboBox->currentText();
-}
-
-
-float drumkv1widget_combo::value (void) const
-{
-	return float(m_pComboBox->currentIndex());
-}
-
-
-// Special combo-box mode accessors.
-void drumkv1widget_combo::insertItems ( int iIndex, const QStringList& items )
-{
-	m_pComboBox->insertItems(iIndex, items);
-
-	setMinimum(0.0f);
-
-	const int iItemCount = m_pComboBox->count();
-	if (iItemCount > 0) {
-		setMaximum(float(iItemCount - 1));
-		setSingleStep(5.0f / float(iItemCount));
-	} else {
-		setMaximum(1.0f);
-		setSingleStep(1.0f);
-	}
-}
-
-
-void drumkv1widget_combo::clear (void)
-{
-	m_pComboBox->clear();
-
-	setMinimum(0.0f);
-	setMaximum(1.0f);
-
-	setSingleStep(1.0f);
-}
-
-
-// Internal widget slots.
-void drumkv1widget_combo::comboBoxValueChanged ( int iComboValue )
-{
-	drumkv1widget_knob::setValue(float(iComboValue));
-}
-
-
-// Reimplemented mouse-wheel stepping.
-void drumkv1widget_combo::wheelEvent ( QWheelEvent *pWheelEvent )
-{
-	const int delta
-		= (pWheelEvent->delta() / 120);
-	if (delta) {
-		float fValue = value() + float(delta);
-		if (fValue < minimum())
-			fValue = minimum();
-		else
-		if (fValue > maximum())
-			fValue = maximum();
-		setValue(fValue);
-	}
-}
-
-
-// end of drumkv1widget_knob.cpp
diff --git a/src/drumkv1widget_lv2.cpp b/src/drumkv1widget_lv2.cpp
index deaf8a9..6023f78 100644
--- a/src/drumkv1widget_lv2.cpp
+++ b/src/drumkv1widget_lv2.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_lv2.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -49,7 +49,7 @@ drumkv1widget_lv2::drumkv1widget_lv2 ( drumkv1_lv2 *pDrumk,
 		m_params_def[i] = true;
 
 	// May initialize the scheduler/work notifier.
-	initSchedNotifier();
+	openSchedNotifier();
 
 	// Initial update, always...
 	refreshElements();
diff --git a/src/drumkv1widget_lv2.h b/src/drumkv1widget_lv2.h
index eca6a3d..24db038 100644
--- a/src/drumkv1widget_lv2.h
+++ b/src/drumkv1widget_lv2.h
@@ -1,7 +1,7 @@
 // drumkv1widget_lv2.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2016, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
diff --git a/src/drumkv1widget_param.cpp b/src/drumkv1widget_param.cpp
new file mode 100644
index 0000000..98ccc42
--- /dev/null
+++ b/src/drumkv1widget_param.cpp
@@ -0,0 +1,929 @@
+// drumkv1widget_param.cpp
+//
+/****************************************************************************
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
+
+   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 "drumkv1widget_param.h"
+
+#include <QLabel>
+#include <QLineEdit>
+#include <QComboBox>
+#include <QRadioButton>
+#include <QCheckBox>
+
+#include <QGridLayout>
+#include <QMouseEvent>
+
+#include <math.h>
+
+
+// Integer value round.
+inline int iroundf(float x) { return int(x < 0.0f ? x - 0.5f : x + 0.5f); }
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_dial - A better QDial widget.
+
+drumkv1widget_dial::DialMode
+drumkv1widget_dial::g_dialMode = drumkv1widget_dial::DefaultMode;
+
+// Set knob dial mode behavior.
+void drumkv1widget_dial::setDialMode ( DialMode dialMode )
+	{ g_dialMode = dialMode; }
+
+drumkv1widget_dial::DialMode drumkv1widget_dial::dialMode (void)
+	{ return g_dialMode; }
+
+
+// Constructor.
+drumkv1widget_dial::drumkv1widget_dial ( QWidget *pParent )
+	: QDial(pParent), m_bMousePressed(false), m_fLastDragValue(0.0f)
+{
+}
+
+
+// Mouse angle determination.
+float drumkv1widget_dial::mouseAngle ( const QPoint& pos )
+{
+	const float dx = pos.x() - (width() >> 1);
+	const float dy = (height() >> 1) - pos.y();
+	return 180.0f * ::atan2f(dx, dy) / float(M_PI);
+}
+
+
+// Alternate mouse behavior event handlers.
+void drumkv1widget_dial::mousePressEvent ( QMouseEvent *pMouseEvent )
+{
+	if (g_dialMode == DefaultMode) {
+		QDial::mousePressEvent(pMouseEvent);
+	} else if (pMouseEvent->button() == Qt::LeftButton) {
+		m_bMousePressed = true;
+		m_posMouse = pMouseEvent->pos();
+		m_fLastDragValue = float(value());
+		emit sliderPressed();
+	}
+}
+
+
+void drumkv1widget_dial::mouseMoveEvent ( QMouseEvent *pMouseEvent )
+{
+	if (g_dialMode == DefaultMode) {
+		QDial::mouseMoveEvent(pMouseEvent);
+		return;
+	}
+
+	if (!m_bMousePressed)
+		return;
+
+	const QPoint& pos = pMouseEvent->pos();
+	const int dx = pos.x() - m_posMouse.x();
+	const int dy = pos.y() - m_posMouse.y();
+	float fAngleDelta =  mouseAngle(pos) - mouseAngle(m_posMouse);
+	int iNewValue = value();
+
+	switch (g_dialMode)	{
+	case LinearMode:
+		iNewValue = int(m_fLastDragValue) + dx - dy;
+		break;
+	case AngularMode:
+	default:
+		// Forget about the drag origin to be robust on full rotations
+		if (fAngleDelta > +180.0f) fAngleDelta -= 360.0f;
+		else
+		if (fAngleDelta < -180.0f) fAngleDelta += 360.0f;
+		m_fLastDragValue += float(maximum() - minimum()) * fAngleDelta / 270.0f;
+		if (m_fLastDragValue > float(maximum()))
+			m_fLastDragValue = float(maximum());
+		else
+		if (m_fLastDragValue < float(minimum()))
+			m_fLastDragValue = float(minimum());
+		m_posMouse = pos;
+		iNewValue = int(m_fLastDragValue + 0.5f);
+		break;
+	}
+
+	setValue(iNewValue);
+	update();
+
+	emit sliderMoved(value());
+}
+
+
+void drumkv1widget_dial::mouseReleaseEvent ( QMouseEvent *pMouseEvent )
+{
+	if (g_dialMode == DefaultMode
+		&& pMouseEvent->button() != Qt::MidButton) {
+		QDial::mouseReleaseEvent(pMouseEvent);
+	} else if (m_bMousePressed) {
+		m_bMousePressed = false;
+	}
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_param - Custom composite widget.
+//
+
+// Constructor.
+drumkv1widget_param::drumkv1widget_param ( QWidget *pParent ) : QWidget(pParent)
+{
+	const QFont& font = QWidget::font();
+	const QFont font2(font.family(), font.pointSize() - 2);
+	QWidget::setFont(font2);
+
+	m_fValue = 0.0f;
+
+	m_fMinimum = 0.0f;
+	m_fMaximum = 1.0f;
+
+	m_fScale = 1.0f;
+
+	resetDefaultValue();
+
+	QWidget::setMaximumSize(QSize(52, 72));
+
+	QGridLayout *pGridLayout = new QGridLayout();
+	pGridLayout->setMargin(0);
+	pGridLayout->setSpacing(0);
+	QWidget::setLayout(pGridLayout);
+}
+
+
+// Accessors.
+void drumkv1widget_param::setText ( const QString& sText )
+{
+	setValue(sText.toFloat());
+}
+
+
+QString drumkv1widget_param::text (void) const
+{
+	return QString::number(value());
+}
+
+
+void drumkv1widget_param::setValue ( float fValue, bool bDefault )
+{
+	QPalette pal;
+
+	if (bDefault) {
+		m_fDefaultValue = fValue;
+		m_iDefaultValue++;
+	}
+	else
+	if (QWidget::isEnabled()
+		&& ::fabsf(fValue - m_fDefaultValue) > 0.0001f) {
+		pal.setColor(QPalette::Base,
+			(pal.window().color().value() < 0x7f
+				? QColor(Qt::darkYellow).darker()
+				: QColor(Qt::yellow).lighter()));
+	}
+
+	QWidget::setPalette(pal);
+
+	if (::fabsf(fValue - m_fValue) > 0.0001f) {
+		m_fValue = fValue;
+		emit valueChanged(m_fValue);
+	}
+}
+
+
+float drumkv1widget_param::value (void) const
+{
+	return m_fValue;
+}
+
+
+QString drumkv1widget_param::valueText (void) const
+{
+	return QString::number(value());
+}
+
+
+void drumkv1widget_param::setMaximum ( float fMaximum )
+{
+	m_fMaximum = fMaximum;
+}
+
+float drumkv1widget_param::maximum (void) const
+{
+	return m_fMaximum;
+}
+
+
+void drumkv1widget_param::setMinimum ( float fMinimum )
+{
+	m_fMinimum = fMinimum;
+}
+
+float drumkv1widget_param::minimum (void) const
+{
+	return m_fMinimum;
+}
+
+
+void drumkv1widget_param::resetDefaultValue (void)
+{
+	m_fDefaultValue = 0.0f;
+	m_iDefaultValue = 0;
+}
+
+
+bool drumkv1widget_param::isDefaultValue (void) const
+{
+	return (m_iDefaultValue > 0);
+}
+
+
+void drumkv1widget_param::setDefaultValue ( float fDefaultValue )
+{
+	m_fDefaultValue = fDefaultValue;
+	m_iDefaultValue++;
+}
+
+
+float drumkv1widget_param::defaultValue (void) const
+{
+	return m_fDefaultValue;
+}
+
+
+// Mouse behavior event handler.
+void drumkv1widget_param::mousePressEvent ( QMouseEvent *pMouseEvent )
+{
+	if (pMouseEvent->button() == Qt::MidButton) {
+		if (m_iDefaultValue < 1) {
+			m_fDefaultValue = 0.5f * (maximum() + minimum());
+			m_iDefaultValue++;
+		}
+		setValue(m_fDefaultValue);
+	}
+
+	QWidget::mousePressEvent(pMouseEvent);
+}
+
+
+// Scale multiplier accessors.
+void drumkv1widget_param::setScale ( float fScale )
+{
+	m_fScale = fScale;
+}
+
+
+float drumkv1widget_param::scale (void) const
+{
+	return m_fScale;
+}
+
+
+// Scale/value converters.
+float drumkv1widget_param::scaleFromValue ( float fValue ) const
+{
+	return (m_fScale * fValue);
+}
+
+
+float drumkv1widget_param::valueFromScale ( float fScale ) const
+{
+	return (fScale / m_fScale);
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_knob - Custom knob/dial widget.
+//
+
+// Constructor.
+drumkv1widget_knob::drumkv1widget_knob ( QWidget *pParent ) : drumkv1widget_param(pParent)
+{
+	m_pLabel = new QLabel();
+	m_pLabel->setAlignment(Qt::AlignCenter);
+
+	m_pDial = new drumkv1widget_dial();
+	m_pDial->setNotchesVisible(true);
+	m_pDial->setMaximumSize(QSize(48, 48));
+
+	QGridLayout *pGridLayout
+		= static_cast<QGridLayout *> (drumkv1widget_param::layout());
+	pGridLayout->addWidget(m_pLabel, 0, 0, 1, 3);
+	pGridLayout->addWidget(m_pDial,  1, 0, 1, 3);
+	pGridLayout->setAlignment(m_pDial, Qt::AlignVCenter | Qt::AlignHCenter);
+
+	QObject::connect(m_pDial,
+		SIGNAL(valueChanged(int)),
+		SLOT(dialValueChanged(int)));
+}
+
+
+void drumkv1widget_knob::setText ( const QString& sText )
+{
+	m_pLabel->setText(sText);
+}
+
+
+QString drumkv1widget_knob::text (void) const
+{
+	return m_pLabel->text();
+}
+
+
+void drumkv1widget_knob::setValue ( float fValue, bool bDefault )
+{
+	const bool bDialBlock = m_pDial->blockSignals(true);
+	drumkv1widget_param::setValue(fValue, bDefault);
+	m_pDial->setValue(scaleFromValue(fValue));
+	m_pDial->blockSignals(bDialBlock);
+}
+
+
+void drumkv1widget_knob::setMaximum ( float fMaximum )
+{
+	drumkv1widget_param::setMaximum(fMaximum);
+	m_pDial->setMaximum(scaleFromValue(fMaximum));
+}
+
+
+void drumkv1widget_knob::setMinimum ( float fMinimum )
+{
+	drumkv1widget_param::setMinimum(fMinimum);
+	m_pDial->setMinimum(scaleFromValue(fMinimum));
+}
+
+
+// Scale-step accessors.
+void drumkv1widget_knob::setSingleStep ( float fSingleStep )
+{
+	m_pDial->setSingleStep(scaleFromValue(fSingleStep));
+}
+
+
+float drumkv1widget_knob::singleStep (void) const
+{
+	return valueFromScale(m_pDial->singleStep());
+}
+
+
+// Dial change slot.
+void drumkv1widget_knob::dialValueChanged ( int iDialValue )
+{
+	setValue(valueFromScale(iDialValue));
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_edit - A better QDoubleSpinBox widget.
+
+drumkv1widget_edit::EditMode
+drumkv1widget_edit::g_editMode = drumkv1widget_edit::DefaultMode;
+
+// Set spin-box edit mode behavior.
+void drumkv1widget_edit::setEditMode ( EditMode editMode )
+	{ g_editMode = editMode; }
+
+drumkv1widget_edit::EditMode drumkv1widget_edit::editMode (void)
+	{ return g_editMode; }
+
+
+// Constructor.
+drumkv1widget_edit::drumkv1widget_edit ( QWidget *pParent )
+	: QDoubleSpinBox(pParent), m_iTextChanged(0)
+{
+	QObject::connect(lineEdit(),
+		SIGNAL(textChanged(const QString&)),
+		SLOT(lineEditTextChanged(const QString&)));
+	QObject::connect(this,
+		SIGNAL(editingFinished()),
+		SLOT(spinBoxEditingFinished()));
+	QObject::connect(this,
+		SIGNAL(valueChanged(double)),
+		SLOT(spinBoxValueChanged(double)));
+}
+
+
+// Alternate value change behavior handlers.
+void drumkv1widget_edit::lineEditTextChanged ( const QString& )
+{
+	if (g_editMode == DeferredMode)
+		++m_iTextChanged;
+}
+
+
+void drumkv1widget_edit::spinBoxEditingFinished (void)
+{
+	if (g_editMode == DeferredMode) {
+		m_iTextChanged = 0;
+		emit valueChangedEx(value());
+	}
+}
+
+
+void drumkv1widget_edit::spinBoxValueChanged ( double spinValue )
+{
+	if (g_editMode != DeferredMode || m_iTextChanged == 0)
+		emit valueChangedEx(spinValue);
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_spin - Custom knob/spin-box widget.
+//
+
+// Constructor.
+drumkv1widget_spin::drumkv1widget_spin ( QWidget *pParent )
+	: drumkv1widget_knob(pParent)
+{
+	m_pSpinBox = new drumkv1widget_edit();
+	m_pSpinBox->setAccelerated(true);
+	m_pSpinBox->setAlignment(Qt::AlignCenter);
+
+	const QFontMetrics fm(drumkv1widget_knob::font());
+	m_pSpinBox->setMaximumHeight(fm.height() + 6);
+
+	QGridLayout *pGridLayout
+		= static_cast<QGridLayout *> (drumkv1widget_knob::layout());
+	pGridLayout->addWidget(m_pSpinBox, 2, 1, 1, 1);
+
+	setScale(100.0f);
+
+	setMinimum(0.0f);
+	setMaximum(1.0f);
+
+	setDecimals(1);
+
+	QObject::connect(m_pSpinBox,
+		SIGNAL(valueChangedEx(double)),
+		SLOT(spinBoxValueChanged(double)));
+}
+
+
+// Virtual accessors.
+void drumkv1widget_spin::setValue ( float fValue, bool bDefault )
+{
+	const bool bSpinBlock = m_pSpinBox->blockSignals(true);
+	drumkv1widget_knob::setValue(fValue, bDefault);
+	m_pSpinBox->setValue(scaleFromValue(fValue));
+	m_pSpinBox->blockSignals(bSpinBlock);
+}
+
+
+void drumkv1widget_spin::setMaximum ( float fMaximum )
+{
+	m_pSpinBox->setMaximum(scaleFromValue(fMaximum));
+	drumkv1widget_knob::setMaximum(fMaximum);
+}
+
+
+void drumkv1widget_spin::setMinimum ( float fMinimum )
+{
+	m_pSpinBox->setMinimum(scaleFromValue(fMinimum));
+	drumkv1widget_knob::setMinimum(fMinimum);
+}
+
+
+QString drumkv1widget_spin::valueText (void) const
+{
+	return QString::number(m_pSpinBox->value(), 'f', 1);
+}
+
+
+// Internal widget slots.
+void drumkv1widget_spin::spinBoxValueChanged ( double spinValue )
+{
+	drumkv1widget_knob::setValue(valueFromScale(float(spinValue)));
+}
+
+
+// Special value text (minimum)
+void drumkv1widget_spin::setSpecialValueText ( const QString& sText )
+{
+	m_pSpinBox->setSpecialValueText(sText);
+}
+
+
+QString drumkv1widget_spin::specialValueText (void) const
+{
+	return m_pSpinBox->specialValueText();
+}
+
+
+bool drumkv1widget_spin::isSpecialValue (void) const
+{
+	return (m_pSpinBox->minimum() >= m_pSpinBox->value());
+}
+
+
+// Decimal digits allowed.
+void drumkv1widget_spin::setDecimals ( int iDecimals )
+{
+	m_pSpinBox->setDecimals(iDecimals);
+	m_pSpinBox->setSingleStep(::powf(10.0f, - float(iDecimals)));
+
+	setSingleStep(0.1f);
+}
+
+int drumkv1widget_spin::decimals (void) const
+{
+	return m_pSpinBox->decimals();
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_combo - Custom knob/combo-box widget.
+//
+
+// Constructor.
+drumkv1widget_combo::drumkv1widget_combo ( QWidget *pParent )
+	: drumkv1widget_knob(pParent)
+{
+	m_pComboBox = new QComboBox();
+
+	const QFontMetrics fm(drumkv1widget_knob::font());
+	m_pComboBox->setMaximumHeight(fm.height() + 6);
+
+	QGridLayout *pGridLayout
+		= static_cast<QGridLayout *> (drumkv1widget_knob::layout());
+	pGridLayout->addWidget(m_pComboBox, 2, 0, 1, 3);
+
+//	setScale(1.0f);
+
+	QObject::connect(m_pComboBox,
+		SIGNAL(activated(int)),
+		SLOT(comboBoxValueChanged(int)));
+}
+
+
+// Virtual accessors.
+void drumkv1widget_combo::setValue ( float fValue, bool bDefault )
+{
+	const bool bComboBlock = m_pComboBox->blockSignals(true);
+	drumkv1widget_knob::setValue(fValue, bDefault);
+	m_pComboBox->setCurrentIndex(iroundf(fValue));
+	m_pComboBox->blockSignals(bComboBlock);
+}
+
+
+QString drumkv1widget_combo::valueText (void) const
+{
+	return m_pComboBox->currentText();
+}
+
+
+// Special combo-box mode accessors.
+void drumkv1widget_combo::insertItems ( int iIndex, const QStringList& items )
+{
+	m_pComboBox->insertItems(iIndex, items);
+
+	setMinimum(0.0f);
+
+	const int iItemCount = m_pComboBox->count();
+	if (iItemCount > 0)
+		setMaximum(float(iItemCount - 1));
+	else
+		setMaximum(1.0f);
+
+	setSingleStep(1.0f);
+}
+
+
+void drumkv1widget_combo::clear (void)
+{
+	m_pComboBox->clear();
+
+	setMinimum(0.0f);
+	setMaximum(1.0f);
+
+	setSingleStep(1.0f);
+}
+
+
+// Internal widget slots.
+void drumkv1widget_combo::comboBoxValueChanged ( int iComboValue )
+{
+	drumkv1widget_knob::setValue(float(iComboValue));
+}
+
+
+// Reimplemented mouse-wheel stepping.
+void drumkv1widget_combo::wheelEvent ( QWheelEvent *pWheelEvent )
+{
+	const int delta
+		= (pWheelEvent->delta() / 120);
+	if (delta) {
+		float fValue = value() + float(delta);
+		if (fValue < minimum())
+			fValue = minimum();
+		else
+		if (fValue > maximum())
+			fValue = maximum();
+		setValue(fValue);
+	}
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_param_style - Custom widget style.
+//
+
+#include <QProxyStyle>
+#include <QPainter>
+#include <QIcon>
+
+
+class drumkv1widget_param_style : public QProxyStyle
+{
+public:
+
+	// Constructor.
+	drumkv1widget_param_style() : QProxyStyle()
+	{
+		m_icon.addPixmap(
+			QPixmap(":/images/ledOff.png"), QIcon::Normal, QIcon::Off);
+		m_icon.addPixmap(
+			QPixmap(":/images/ledOn.png"), QIcon::Normal, QIcon::On);
+	}
+
+
+	// Hints override.
+	int styleHint(StyleHint hint, const QStyleOption *option,
+		const QWidget *widget, QStyleHintReturn *retdata) const
+	{
+		if (hint == QStyle::SH_UnderlineShortcut)
+			return 0;
+		else
+			return QProxyStyle::styleHint(hint, option, widget, retdata);
+	}
+
+	// Paint job.
+	void drawPrimitive(PrimitiveElement element,
+		const QStyleOption *option,
+		QPainter *painter, const QWidget *widget) const
+	{
+		if (element == PE_IndicatorRadioButton ||
+			element == PE_IndicatorCheckBox) {
+			const QRect& rect = option->rect;
+			if (option->state & State_Enabled) {
+				if (option->state & State_On)
+					m_icon.paint(painter, rect,
+						Qt::AlignCenter, QIcon::Normal, QIcon::On);
+				else
+			//	if (option->state & State_Off)
+					m_icon.paint(painter, rect,
+						Qt::AlignCenter, QIcon::Normal, QIcon::Off);
+			} else {
+				m_icon.paint(painter, rect,
+					Qt::AlignCenter, QIcon::Disabled, QIcon::Off);
+			}
+		}
+		else
+		QProxyStyle::drawPrimitive(element, option, painter, widget);
+	}
+
+	// Spiced up text margins
+	void drawItemText(QPainter *painter, const QRect& rectangle,
+		int alignment, const QPalette& palette, bool enabled,
+		const QString& text, QPalette::ColorRole textRole) const
+	{
+		QRect rect = rectangle;
+		rect.setLeft(rect.left() - 4);
+		rect.setRight(rect.right() + 4);
+		QProxyStyle::drawItemText(painter, rect,
+			alignment, palette, enabled, text, textRole);
+	}
+
+	static void addRef ()
+		{ if (++g_iRefCount == 1) g_pStyle = new drumkv1widget_param_style(); }
+
+	static void releaseRef ()
+		{ if (--g_iRefCount == 0) { delete g_pStyle; g_pStyle = NULL; } }
+
+	static drumkv1widget_param_style *getRef ()
+		{ return g_pStyle; }
+
+private:
+
+	QIcon m_icon;
+
+	static drumkv1widget_param_style *g_pStyle;
+	static unsigned int g_iRefCount;
+};
+
+
+drumkv1widget_param_style *drumkv1widget_param_style::g_pStyle = NULL;
+unsigned int drumkv1widget_param_style::g_iRefCount = 0;
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_radio - Custom radio/button widget.
+//
+
+// Constructor.
+drumkv1widget_radio::drumkv1widget_radio ( QWidget *pParent )
+	: drumkv1widget_param(pParent), m_group(this)
+{
+	drumkv1widget_param_style::addRef();
+#if 0
+	drumkv1widget_param::setStyleSheet(
+	//	"QRadioButton::indicator { width: 16px; height: 16px; }"
+		"QRadioButton::indicator::unchecked { image: url(:/images/ledOff.png); }"
+		"QRadioButton::indicator::checked   { image: url(:/images/ledOn.png);  }"
+	);
+#endif
+
+	const QFont& font = drumkv1widget_param::font();
+	const QFont font1(font.family(), font.pointSize() - 1);
+	QWidget::setFont(font1);
+
+	QObject::connect(&m_group,
+		SIGNAL(buttonClicked(int)),
+		SLOT(radioGroupValueChanged(int)));
+}
+
+
+// Destructor.
+drumkv1widget_radio::~drumkv1widget_radio (void)
+{
+	drumkv1widget_param_style::releaseRef();
+}
+
+
+// Virtual accessors.
+void drumkv1widget_radio::setValue ( float fValue, bool bDefault )
+{
+	const int iRadioValue = iroundf(fValue);
+	QRadioButton *pRadioButton
+		= static_cast<QRadioButton *> (m_group.button(iRadioValue));
+	if (pRadioButton) {
+		const bool bRadioBlock = pRadioButton->blockSignals(true);
+		drumkv1widget_param::setValue(float(iRadioValue), bDefault);
+		pRadioButton->setChecked(true);
+		pRadioButton->blockSignals(bRadioBlock);
+	}
+}
+
+
+QString drumkv1widget_radio::valueText (void) const
+{
+	QString sValueText;
+	const int iRadioValue = iroundf(value());
+	QRadioButton *pRadioButton
+		= static_cast<QRadioButton *> (m_group.button(iRadioValue));
+	if (pRadioButton)
+		sValueText = pRadioButton->text();
+	return sValueText;
+}
+
+
+// Special combo-box mode accessors.
+void drumkv1widget_radio::insertItems ( int iIndex, const QStringList& items )
+{
+	QGridLayout *pGridLayout
+		= static_cast<QGridLayout *> (drumkv1widget_param::layout());
+	const QString sToolTipMask(drumkv1widget_param::toolTip() + ": %1");
+	QStringListIterator iter(items);
+	while (iter.hasNext()) {
+		const QString& sValueText = iter.next();
+		QRadioButton *pRadioButton = new QRadioButton(sValueText);
+		pRadioButton->setStyle(drumkv1widget_param_style::getRef());
+		pRadioButton->setToolTip(sToolTipMask.arg(sValueText));
+		pGridLayout->addWidget(pRadioButton, iIndex, 0);
+		m_group.addButton(pRadioButton, iIndex);
+		++iIndex;
+	}
+
+	setMinimum(0.0f);
+
+	const QList<QAbstractButton *> list = m_group.buttons();
+	const int iRadioCount = list.count();
+	if (iRadioCount > 0)
+		setMaximum(float(iRadioCount - 1));
+	else
+		setMaximum(1.0f);
+}
+
+
+void drumkv1widget_radio::clear (void)
+{
+	const QList<QAbstractButton *> list = m_group.buttons();
+	QListIterator<QAbstractButton *> iter(list);
+	while (iter.hasNext()) {
+		QRadioButton *pRadioButton
+			= static_cast<QRadioButton *> (iter.next());
+		if (pRadioButton)
+			m_group.removeButton(pRadioButton);
+	}
+
+	setMinimum(0.0f);
+	setMaximum(1.0f);
+}
+
+
+void drumkv1widget_radio::radioGroupValueChanged ( int iRadioValue )
+{
+	drumkv1widget_param::setValue(float(iRadioValue));
+}
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_check - Custom check-box widget.
+//
+
+// Constructor.
+drumkv1widget_check::drumkv1widget_check ( QWidget *pParent )
+	: drumkv1widget_param(pParent)
+{
+	drumkv1widget_param_style::addRef();
+#if 0
+	drumkv1widget_param::setStyleSheet(
+	//	"QCheckBox::indicator { width: 16px; height: 16px; }"
+		"QCheckBox::indicator::unchecked { image: url(:/images/ledOff.png); }"
+		"QCheckBox::indicator::checked   { image: url(:/images/ledOn.png);  }"
+	);
+#endif
+	m_pCheckBox = new QCheckBox();
+	m_pCheckBox->setStyle(drumkv1widget_param_style::getRef());
+
+	m_alignment = Qt::AlignHCenter | Qt::AlignVCenter;
+
+	QGridLayout *pGridLayout
+		= static_cast<QGridLayout *> (drumkv1widget_param::layout());
+	pGridLayout->addWidget(m_pCheckBox, 0, 0);
+	pGridLayout->setAlignment(m_pCheckBox, m_alignment);
+
+	drumkv1widget_param::setMaximumSize(QSize(72, 72));
+
+	QObject::connect(m_pCheckBox,
+		SIGNAL(toggled(bool)),
+		SLOT(checkBoxValueChanged(bool)));
+}
+
+
+// Destructor.
+drumkv1widget_check::~drumkv1widget_check (void)
+{
+	drumkv1widget_param_style::releaseRef();
+}
+
+
+// Accessors.
+void drumkv1widget_check::setText ( const QString& sText )
+{
+	m_pCheckBox->setText(sText);
+}
+
+
+QString drumkv1widget_check::text (void) const
+{
+	return m_pCheckBox->text();
+}
+
+
+void drumkv1widget_check::setAlignment ( Qt::Alignment alignment )
+{
+	m_alignment = alignment;
+
+	QLayout *pLayout = drumkv1widget_param::layout();
+	if (pLayout)
+		pLayout->setAlignment(m_pCheckBox, m_alignment);
+}
+
+
+Qt::Alignment drumkv1widget_check::alignment (void) const
+{
+	return m_alignment;
+}
+
+
+// Virtual accessors.
+void drumkv1widget_check::setValue ( float fValue, bool bDefault )
+{
+	const bool bCheckValue = (fValue > 0.5f * (maximum() + minimum()));
+	const bool bCheckBlock = m_pCheckBox->blockSignals(true);
+	drumkv1widget_param::setValue(bCheckValue ? maximum() : minimum(), bDefault);
+	m_pCheckBox->setChecked(bCheckValue);
+	m_pCheckBox->blockSignals(bCheckBlock);
+}
+
+
+void drumkv1widget_check::checkBoxValueChanged ( bool bCheckValue )
+{
+	drumkv1widget_param::setValue(bCheckValue ? maximum() : minimum());
+}
+
+
+// end of drumkv1widget_param.cpp
diff --git a/src/drumkv1widget_knob.h b/src/drumkv1widget_param.h
similarity index 58%
rename from src/drumkv1widget_knob.h
rename to src/drumkv1widget_param.h
index d5bcf16..ccfe11d 100644
--- a/src/drumkv1widget_knob.h
+++ b/src/drumkv1widget_param.h
@@ -1,7 +1,7 @@
-// drumkv1widget_knob.h
+// drumkv1widget_param.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2015, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -19,17 +19,19 @@
 
 *****************************************************************************/
 
-#ifndef __drumkv1widget_knob_h
-#define __drumkv1widget_knob_h
+#ifndef __drumkv1widget_param_h
+#define __drumkv1widget_param_h
 
 #include <QWidget>
 #include <QDial>
+#include <QDoubleSpinBox>
+#include <QButtonGroup>
 
 
 // Forward declarations.
 class QLabel;
-class QDoubleSpinBox;
 class QComboBox;
+class QCheckBox;
 
 
 //-------------------------------------------------------------------------
@@ -79,20 +81,20 @@ private:
 
 
 //-------------------------------------------------------------------------
-// drumkv1widget_knob - Custom composite widget.
+// drumkv1widget_param - Custom composite widget.
 
-class drumkv1widget_knob : public QWidget
+class drumkv1widget_param : public QWidget
 {
 	Q_OBJECT
 
 public:
 
 	// Constructor.
-	drumkv1widget_knob(QWidget *pParent = 0);
+	drumkv1widget_param(QWidget *pParent = 0);
 
 	// Accessors.
-	void setText(const QString& sText);
-	QString text() const;
+	virtual void setText(const QString& sText);
+	virtual QString text() const;
 
 	virtual void setMaximum(float fMaximum);
 	float maximum() const;
@@ -100,21 +102,21 @@ public:
 	virtual void setMinimum(float fMinimum);
 	float minimum() const;
 
-	virtual void setSingleStep(float fSingleStep);
-	float singleStep() const;
-
 	void resetDefaultValue();
+	bool isDefaultValue() const;
 	void setDefaultValue(float fDefaultValue);
 	float defaultValue() const;
 
 	virtual QString valueText() const;
-	virtual float value() const;
+	float value() const;
 
+	// Scale multiplier accessors.
 	void setScale(float fScale);
 	float scale() const;
 
 public slots:
 
+	// Virtual accessor.
 	virtual void setValue(float fValue, bool bDefault = false);
 
 signals:
@@ -122,11 +124,6 @@ signals:
 	// Change signal.
 	void valueChanged(float);
 
-protected slots:
-
-	// Change slot.
-	void dialValueChanged(int);
-
 protected:
 
 	// Mouse behavior event handler.
@@ -138,10 +135,12 @@ protected:
 
 private:
 
-	// Widget members.
-	QLabel *m_pLabel;
+	// Current value.
+	float m_fValue;
 
-	drumkv1widget_dial *m_pDial;
+	// Current value range.
+	float m_fMinimum;
+	float m_fMaximum;
 
 	// Default value.
 	float m_fDefaultValue;
@@ -153,6 +152,93 @@ private:
 
 
 //-------------------------------------------------------------------------
+// drumkv1widget_knob - Custom knob/dial widget.
+
+class drumkv1widget_knob : public drumkv1widget_param
+{
+	Q_OBJECT
+
+public:
+
+	// Constructor.
+	drumkv1widget_knob(QWidget *pParent = 0);
+
+	// Accessors.
+	void setText(const QString& sText);
+	QString text() const;
+
+	void setMaximum(float fMaximum);
+	void setMinimum(float fMinimum);
+
+public slots:
+
+	// Virtual accessor.
+	void setValue(float fValue, bool bDefault = false);
+
+protected slots:
+
+	// Dial change slot.
+	void dialValueChanged(int);
+
+protected:
+
+	// Scale-step accessors.
+	void setSingleStep(float fSingleStep);
+	float singleStep() const;
+
+private:
+
+	// Widget members.
+	QLabel *m_pLabel;
+
+	drumkv1widget_dial *m_pDial;
+};
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_edit - A better QDoubleSpinBox widget.
+
+class drumkv1widget_edit : public QDoubleSpinBox
+{
+	Q_OBJECT
+
+public:
+
+	// Constructor.
+	drumkv1widget_edit(QWidget *pParent = 0);
+
+	// Edit mode behavior:
+	// DefaultMode - default (immediate value changes) behavior.
+	// DeferredMode - deferred value changes (to when editing is finished).
+	enum EditMode { DefaultMode = 0, DeferredMode };
+
+	// Set spin-box edit mode behavior.
+	static void setEditMode(EditMode editMode);
+	static EditMode editMode();
+
+protected slots:
+
+	// Alternate value change behavior handlers.
+	void lineEditTextChanged(const QString&);
+	void spinBoxEditingFinished();
+	void spinBoxValueChanged(double);
+
+signals:
+
+	// Alternate value change signal.
+	void valueChangedEx(double);
+
+private:
+
+	// Alternate edit behavior tracking.
+	int m_iTextChanged;
+
+	// Spin-box edit mode behavior.
+	static EditMode g_editMode;
+};
+
+
+//-------------------------------------------------------------------------
 // drumkv1widget_spin - Custom knob/spin-box widget.
 
 class drumkv1widget_spin : public drumkv1widget_knob
@@ -168,10 +254,7 @@ public:
 	void setMaximum(float fMaximum);
 	void setMinimum(float fMinimum);
 
-	void setSingleStep(float fSingleStep);
-
 	QString valueText() const;
-	float value() const;
 
 	// Specialized accessors.
 	void setSpecialValueText(const QString& sText);
@@ -195,7 +278,7 @@ protected slots:
 private:
 
 	// Widget members.
-	QDoubleSpinBox *m_pSpinBox;
+	drumkv1widget_edit *m_pSpinBox;
 };
 
 
@@ -213,7 +296,6 @@ public:
 
 	// Virtual accessors.
 	QString valueText() const;
-	float value() const;
 
 	// Specialized accessors.
 	void insertItems(int iIndex, const QStringList& items);
@@ -241,6 +323,86 @@ private:
 };
 
 
-#endif  // __drumkv1widget_knob_h
+//-------------------------------------------------------------------------
+// drumkv1widget_radio - Custom radio-button widget.
+
+class drumkv1widget_radio : public drumkv1widget_param
+{
+	Q_OBJECT
+
+public:
+
+	// Constructor.
+	drumkv1widget_radio(QWidget *pParent = 0);
+
+	// Desstructor.
+	~drumkv1widget_radio();
+
+	// Virtual accessors.
+	QString valueText() const;
+
+	// Specialized accessors.
+	void insertItems(int iIndex, const QStringList& items);
+	void clear();
+
+public slots:
+
+	// Virtual accessor.
+	void setValue(float fValue, bool bDefault = false);
+
+protected slots:
+
+	// Change slot.
+	void radioGroupValueChanged(int);
+
+private:
+
+	// Widget members.
+	QButtonGroup m_group;
+};
+
+
+//-------------------------------------------------------------------------
+// drumkv1widget_check - Custom check-box widget.
+
+class drumkv1widget_check : public drumkv1widget_param
+{
+	Q_OBJECT
+
+public:
+
+	// Constructor.
+	drumkv1widget_check(QWidget *pParent = 0);
+
+	// Desstructor.
+	~drumkv1widget_check();
+
+	// Accessors.
+	void setText(const QString& sText);
+	QString text() const;
+
+	void setAlignment(Qt::Alignment alignment);
+	Qt::Alignment alignment() const;
+
+public slots:
+
+	// Virtual accessor.
+	void setValue(float fValue, bool bDefault = false);
+
+protected slots:
+
+	// Change slot.
+	void checkBoxValueChanged(bool);
+
+private:
+
+	// Widget members.
+	QCheckBox *m_pCheckBox;
+
+	Qt::Alignment m_alignment;
+};
+
+
+#endif  // __drumkv1widget_param_h
 
-// end of drumkv1widget_knob.h
+// end of drumkv1widget_param.h
diff --git a/src/drumkv1widget_status.cpp b/src/drumkv1widget_status.cpp
index dec24b0..fae93f9 100644
--- a/src/drumkv1widget_status.cpp
+++ b/src/drumkv1widget_status.cpp
@@ -1,7 +1,7 @@
 // drumkv1widget_status.cpp
 //
 /****************************************************************************
-   Copyright (C) 2012-2014, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -22,6 +22,9 @@
 #include "drumkv1widget_status.h"
 
 #include <QLabel>
+#include <QIcon>
+#include <QPixmap>
+#include <QHBoxLayout>
 
 
 //-------------------------------------------------------------------------
@@ -32,6 +35,44 @@
 drumkv1widget_status::drumkv1widget_status ( QWidget *pParent )
 	: QStatusBar (pParent)
 {
+	QIcon icon;
+
+	icon.addPixmap(
+		QPixmap(":/images/ledOff.png"), QIcon::Normal, QIcon::Off);
+	icon.addPixmap(
+		QPixmap(":/images/ledOn.png"), QIcon::Normal, QIcon::On);
+
+	m_midiInLed[0] = new QPixmap(
+		icon.pixmap(16, 16, QIcon::Normal, QIcon::Off));
+	m_midiInLed[1] = new QPixmap(
+		icon.pixmap(16, 16, QIcon::Normal, QIcon::On));
+
+	const QString sMidiIn(tr("MIDI In"));
+
+	QWidget *pMidiInWidget = new QWidget();
+	pMidiInWidget->setToolTip(tr("%1 status").arg(sMidiIn));
+
+	QHBoxLayout *pMidiInLayout = new QHBoxLayout();
+	pMidiInLayout->setMargin(0);
+	pMidiInLayout->setSpacing(0);
+
+	m_pMidiInLedLabel = new QLabel();
+	m_pMidiInLedLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+	m_pMidiInLedLabel->setPixmap(*m_midiInLed[0]);
+	m_pMidiInLedLabel->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
+	m_pMidiInLedLabel->setAutoFillBackground(true);
+	pMidiInLayout->addWidget(m_pMidiInLedLabel);
+
+	QLabel *pMidiInTextLabel = new QLabel(sMidiIn);
+	pMidiInTextLabel->setMargin(2);
+	pMidiInTextLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+	pMidiInTextLabel->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
+	pMidiInTextLabel->setAutoFillBackground(true);
+	pMidiInLayout->addWidget(pMidiInTextLabel);
+
+	pMidiInWidget->setLayout(pMidiInLayout);
+	QStatusBar::addWidget(pMidiInWidget);
+
 	const QFontMetrics fm(QStatusBar::font());
 	m_pModifiedLabel = new QLabel();
 	m_pModifiedLabel->setAlignment(Qt::AlignHCenter);
@@ -42,19 +83,27 @@ drumkv1widget_status::drumkv1widget_status ( QWidget *pParent )
 }
 
 
+// Destructor.
+drumkv1widget_status::~drumkv1widget_status (void)
+{
+	delete m_midiInLed[1];
+	delete m_midiInLed[0];
+}
+
+
 // Permanent widgets accessors.
-void drumkv1widget_status::setModified ( bool bModified )
+void drumkv1widget_status::midiInLed ( bool bMidiInLed )
 {
-	if (bModified)
-		m_pModifiedLabel->setText(tr("MOD"));
-	else
-		m_pModifiedLabel->clear();
+	m_pMidiInLedLabel->setPixmap(*m_midiInLed[bMidiInLed ? 1 : 0]);
 }
 
 
-bool drumkv1widget_status::isModified (void) const
+void drumkv1widget_status::modified ( bool bModified )
 {
-	return !m_pModifiedLabel->text().isEmpty();
+	if (bModified)
+		m_pModifiedLabel->setText(tr("MOD"));
+	else
+		m_pModifiedLabel->clear();
 }
 
 
diff --git a/src/drumkv1widget_status.h b/src/drumkv1widget_status.h
index c239594..20d00b6 100644
--- a/src/drumkv1widget_status.h
+++ b/src/drumkv1widget_status.h
@@ -1,7 +1,7 @@
 // drumkv1widget_status.h
 //
 /****************************************************************************
-   Copyright (C) 2012-2014, rncbc aka Rui Nuno Capela. All rights reserved.
+   Copyright (C) 2012-2017, rncbc aka Rui Nuno Capela. All rights reserved.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
@@ -24,8 +24,10 @@
 
 #include <QStatusBar>
 
+
 // Forward declarations.
 class QLabel;
+class QPixmap;
 
 
 //-------------------------------------------------------------------------
@@ -40,13 +42,19 @@ public:
 	// Constructor.
 	drumkv1widget_status(QWidget *pParent = 0);
 
+	// Destructor.
+	~drumkv1widget_status();
+
 	// Permanent widgets accessors.
-	void setModified(bool bModified);
-	bool isModified() const;
+	void midiInLed(bool bMidiInLed);
+	void modified(bool bModified);
 
 private:
 
 	// Permanent widgets.
+	QPixmap *m_midiInLed[2];
+
+	QLabel *m_pMidiInLedLabel;
 	QLabel *m_pModifiedLabel;
 };
 
diff --git a/src/images/ledOff.png b/src/images/ledOff.png
new file mode 100644
index 0000000..3cbc284
Binary files /dev/null and b/src/images/ledOff.png differ
diff --git a/src/images/ledOn.png b/src/images/ledOn.png
new file mode 100644
index 0000000..450d737
Binary files /dev/null and b/src/images/ledOn.png differ
diff --git a/src/src_jack.pro b/src/src_jack.pro
index 614489d..1f5f8b2 100644
--- a/src/src_jack.pro
+++ b/src/src_jack.pro
@@ -22,7 +22,7 @@ HEADERS = \
 	drumkv1widget_filt.h \
 	drumkv1widget_sample.h \
 	drumkv1widget_wave.h \
-	drumkv1widget_knob.h \
+	drumkv1widget_param.h \
 	drumkv1widget_preset.h \
 	drumkv1widget_status.h \
 	drumkv1widget_elements.h \
@@ -40,7 +40,7 @@ SOURCES = \
 	drumkv1widget_filt.cpp \
 	drumkv1widget_sample.cpp \
 	drumkv1widget_wave.cpp \
-	drumkv1widget_knob.cpp \
+	drumkv1widget_param.cpp \
 	drumkv1widget_preset.cpp \
 	drumkv1widget_status.cpp \
 	drumkv1widget_elements.cpp \
diff --git a/src/src_lv2ui.pro b/src/src_lv2ui.pro
index 3b5fee6..8ee0dc8 100644
--- a/src/src_lv2ui.pro
+++ b/src/src_lv2ui.pro
@@ -21,7 +21,7 @@ HEADERS = \
 	drumkv1widget_filt.h \
 	drumkv1widget_sample.h \
 	drumkv1widget_wave.h \
-	drumkv1widget_knob.h \
+	drumkv1widget_param.h \
 	drumkv1widget_preset.h \
 	drumkv1widget_status.h \
 	drumkv1widget_elements.h \
@@ -38,7 +38,7 @@ SOURCES = \
 	drumkv1widget_filt.cpp \
 	drumkv1widget_sample.cpp \
 	drumkv1widget_wave.cpp \
-	drumkv1widget_knob.cpp \
+	drumkv1widget_param.cpp \
 	drumkv1widget_preset.cpp \
 	drumkv1widget_status.cpp \
 	drumkv1widget_elements.cpp \

-- 
drumkv1 packaging



More information about the pkg-multimedia-commits mailing list