[SCM] petri-foo/upstream: Imported Upstream version 0.0.2+20110904.gitbdb1bee1

alessio at users.alioth.debian.org alessio at users.alioth.debian.org
Mon Jan 16 11:48:09 UTC 2012


The following commit has been merged in the upstream branch:
commit 6b49f65896d18e1f4035edae979b6cf909bb9677
Author: Alessio Treglia <alessio at debian.org>
Date:   Mon Jan 16 12:45:48 2012 +0100

    Imported Upstream version 0.0.2+20110904.gitbdb1bee1

diff --git a/AUTHORS b/AUTHORS
index 86bd45f..53e0701 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,3 +1,27 @@
+Petri-Foo AUTHORS
+-----------------
+
+* James W. Morris <james at jwm-art.net>
+
+  forker of Specimen to Petri-Foo, primary developer of Petri-Foo
+  added modulation routing, midi-cc as modulation sources, remover
+  of deprecated GUI code, etc, etc, etc.
+
+
+* Brenden Jones <brendan.jones.it at gmail.com>
+
+  Added auto-preview and a keyboard shortcut to sample-selector,
+  and minimum and maximum velocity values for patch.
+
+
+
+Specimen AUTHORS
+----------------
+
+Petri-Foo began as a fork of Specimen.
+
+Specimen was written by the following people:
+
 -*- text -*-
 
 * Pete Bessman <ninjadroid at gazuga.net>
@@ -58,10 +82,3 @@
 * Eric Dantan Rzewnicki <eric at zhevny.com>
 
   maintainer
-
-
-* James W. Morris <james at jwm-art.net>
-
-  forker of Specimen to Petri-Foo, primary developer of Petri-Foo
-  added modulation routing, midi-cc as modulation sources, remover
-  of deprecated GUI code, etc, etc, etc.
diff --git a/AUTHORSRv b/AUTHORSRv
deleted file mode 100644
index 0f33114..0000000
--- a/AUTHORSRv
+++ /dev/null
@@ -1,65 +0,0 @@
--*- text -*-
-
-* Pete Bessman <ninjadroid at gazuga.net>
-
-  Founder, primary developer untill 2005-09-14
-
-
-
-* Alexander <aharvey at ij.net> 
-
-  modified midi.c to treat note-on events with velocity of 0x00 as
-  note-offs.
-
-* Dave Robillard <drobilla at connect.carleton.ca>
-
-  Lash support. Removed Gnome-ui requirement for about dialog.
-
-* David R. Clark <davidrclark at earthlink.net>
-
-  Invaluable testing, feedback, and discussion, as well as some nifty
-  sample generating programs.
-
-* Dimitry Baikov <dsbaikov at gmail.com>
-
-  Added support for MIDI CC events.
-  Update to jack_midi API.
-
-* Lars Luthman <lars.luthman at gmail.com>
-  
-  Jack-midi implementation.
-
-* Loki Davison <ltdav1 at student.monash.edu>
-
-  Sync code, and initial implementation of lots of stuff which got
-  lost during The Cleansing.  Responsible for convincing ninjadroid to
-  keep on hacking.
-
-* Nedko Arnaudov <nedko at arnaudov.name>
-
-  Made specimen window resizeable.
-
-* peter <kickback at users.sourceforge.net>
-
-  Rewrote the build system during the year of limbo.
-
-* Sacha Berger <sacha at woanders.de> 
-
-  updated beef.c to reflect newly gained parameters.
-
-* Thorsten Wilms <t_w_ at freenet.de>
-
-  UI advice, and designed the fansliders used extensively in the GUI.
-
-* Torben Hohn <torbenh at users.sourceforge.net> 
-
-  was an immense help to Pete in optimizing the waveform code (and he
-  pointed out that it needed to be done in the first place).
-
-
-
-
-
-* Eric Dantan Rzewnicki <eric at zhevny.com>
-
-  maintainer
diff --git a/BUGS b/BUGS
index debbf39..a76cdb1 100644
--- a/BUGS
+++ b/BUGS
@@ -1,42 +1,5 @@
-KNOWN BUGS
-==========
+File all bug reports in the bug tracker at https://sourceforge.net/tracker/?group_id=404816
 
-* mixer_mixdown is sometimes called before mixer_set_jack_client
-resulting in a segfault. i've not yet determined if that statement
-is true or not, but it seems likely.
+Thanks.
 
 
-
-
-
-
--------------------------
-the list that follows is inherited from Specimen. i've not actually checked
-if it is still relevant or not - jwm 2011/03/28
-
-Known Bugs
-==========
-
-* Create some patches, load some samples, connect to JACK, and start
-running a MIDI pattern .  Then, flick back and forth between the
-patches quickly (a mousewheel is handy for this).  JACK will fail with
-something along the lines of "unable to execute processing graph
-(invalid file descriptor)".  JACK will stop processing data until
-Specimen is closed (it will exit with a segfault); JACK and all of
-it's clients will then resume normal operation. [This seems to have
-magically disappeared, which I find quite disturbing.  Can anybody
-reproduce it? --pete]
-
-* Sample start/stop and loop points don't behave properly when
-switching audio sample rates.  It's probably time to store the audio
-sample rate along with the session file, BTW.
-
-* Midi syncing appears to be broken with Fedora Core 2's kernel.  If
-you compile --enable-debug you'll see the gory details.
-
-* Midi syncing drifts like a bitch anyway.
-
-* Help -> About -> credits causes a segfault.
-
-* audio settings dialog currently resets jack client name to default
-"specimen" instead of using previously set name.
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..de5169f
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,156 @@
+cmake_minimum_required(VERSION 2.6)
+
+if (COMMAND CMAKE_POLICY)
+  cmake_policy (SET CMP0011 NEW)
+endif (COMMAND CMAKE_POLICY)
+
+project(Petri-Foo)
+
+set(Petri-Foo_VERSION_MAJOR 0)
+set(Petri-Foo_VERSION_MINOR 3)
+set(BINDIR  "bin" CACHE STRING "Where to install binaries")
+set(DATADIR "share/petri-foo" CACHE STRING "Where to install data files")
+set(APPLICATIONS_DIR "share/applications" CACHE STRING "Where to install desktop files")
+set(PIXMAPS_DIR "share/pixmaps" CACHE STRING "Where to install icons")
+set(MIME_DIR "share/mime" CACHE STRING "Where MIME definitions are located")
+set(UpdateMime "ON" CACHE BOOL "Update Mime Database")
+
+mark_as_advanced(EXECUTABLE_OUTPUT_PATH)
+mark_as_advanced(LIBRARY_OUTPUT_PATH)
+mark_as_advanced(CMAKE_BUILD_TYPE)
+mark_as_advanced(CMAKE_INSTALL_PREFIX)
+
+option (BuildForDebug "Include gdb debugging support" ON)
+
+# DEBUG
+set (BuildOptionsDebug "-O0 -ggdb" CACHE STRING "Debug build flags")
+
+# additional compiler flags
+add_definitions(-Wall -Wextra)
+
+if (BuildForDebug)
+    set (CMAKE_BUILD_TYPE "Debug")
+    set (DEBUG 1)
+    set (CMAKE_C_FLAGS_DEBUG ${BuildOptionsDebug})
+    message (STATUS "Building for ${CMAKE_BUILD_TYPE}, flags: ${CMAKE_C_FLAGS_DEBUG}")
+else (BuildForDebug)
+    set (CMAKE_BUILD_TYPE "Release")
+    set (DEBUG 0)
+    set (CMAKE_C_FLAGS_RELEASE ${BuildOptionsBasic})
+    message (STATUS "Building for ${CMAKE_BUILD_TYPE}, flags: ${CMAKE_C_FLAGS_RELEASE}")
+endif (BuildForDebug)
+
+
+# PKG-CONFIG
+find_package(PkgConfig REQUIRED)
+if(PKG_CONFIG_FOUND)
+    message(STATUS "Found pkg-config ${PKG_CONFIG_EXECUTABLE}")
+else(PKG_CONFIG_FOUND)
+    message(FATAL_ERROR "pkg-config required but not found")
+endif(PKG_CONFIG_FOUND)
+
+
+# JACK
+pkg_check_modules(JACK REQUIRED jack>=0.120.0)
+if (JACK_FOUND)
+  message(STATUS "Found jack ${JACK_VERSION}")
+else (JACK_FOUND)
+  message(FATAL_ERROR "Jack >= 0.120.0 not found.")
+endif (JACK_FOUND)
+
+
+# ALSA
+pkg_check_modules(ALSA REQUIRED alsa>=1.0.17)
+if (ALSA_FOUND)
+  message(STATUS "Found alsa ${ALSA_VERSION}")
+else (ALSA_FOUND)
+  message(FATAL_ERROR "alsa not found.")
+endif (ALSA_FOUND)
+
+
+# SNDFILE
+pkg_check_modules (SNDFILE REQUIRED sndfile)
+if (SNDFILE_FOUND)
+    message(STATUS "Found sndfile ${SNDFILE_VERSION}")
+else (SNDFILE_FOUND)
+    message(FATAL_ERROR "sndfile not found")
+endif (SNDFILE_FOUND)
+
+
+# SAMPLERATE
+pkg_check_modules (SAMPLERATE REQUIRED samplerate)
+if (SAMPLERATE_FOUND)
+    message(STATUS "Found secret rabbit code ${SAMPLERATE_VERSION}")
+else (SAMPLERATE_FOUND)
+    message(FATAL_ERROR "secret rabbit code not found")
+endif (SAMPLERATE_FOUND)
+
+
+# GLIB2
+pkg_check_modules (GLIB2 REQUIRED glib-2.0>=2.16)
+if (GLIB2_FOUND)
+  message(STATUS "Found glib2 ${GLIB2_VERSION}")
+else (GLIB2_FOUND)
+  message(FATAL_ERROR "glib2 was not found.")
+endif (GLIB2_FOUND)
+
+
+# GTK2
+pkg_check_modules (GTK2 REQUIRED gtk+-2.0)
+if(GTK2_FOUND)
+    message(STATUS "Found gtk2 ${GTK2_VERSION}")
+else(GTK2_FOUND)
+    message(FATAL_ERROR "gtk2 required but not found")
+endif(GTK2_FOUND)
+
+
+# LIBGNOMECANVAS2
+pkg_check_modules (LIBGNOMECANVAS2 REQUIRED libgnomecanvas-2.0)
+if (LIBGNOMECANVAS2_FOUND)
+  message(STATUS "Found libgnomecanvas2 ${LIBGNOMECANVAS2_VERSION}")
+else (LIBGNOMECANVAS2_FOUND)
+  message(FATAL_ERROR "libgnomecanvas2 was not found.")
+endif (LIBGNOMECANVAS2_FOUND)
+
+
+# LIBXML2
+pkg_check_modules (LIBXML2 REQUIRED libxml-2.0)
+if (LIBXML2_FOUND)
+    message(STATUS "Found libxml2 ${LIBXML2_VERSION}")
+else (LIBXML2_FOUND)
+    message(FATAL_ERROR "libxml2 was not found")
+endif (LIBXML2_FOUND)
+
+
+INCLUDE (CheckIncludeFiles)
+CHECK_INCLUDE_FILES (malloc.h HAVE_MALLOC_H)
+CHECK_INCLUDE_FILES (jack/session.h HAVE_JACK_SESSION_H)
+CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.in
+                ${CMAKE_CURRENT_BINARY_DIR}/config.h )
+
+link_libraries(m)
+
+include_directories ( . )
+
+# Install targets
+
+ADD_SUBDIRECTORY( libpetrifoo )
+ADD_SUBDIRECTORY( libpetrifui )
+ADD_SUBDIRECTORY( libphin )
+ADD_SUBDIRECTORY( gui )
+
+#ADD_SUBDIRECTORY( pixmaps )
+
+install (DIRECTORY pixmaps/ DESTINATION ${DATADIR}/pixmaps
+          FILES_MATCHING PATTERN "*.png")
+install (FILES petri-foo.xml DESTINATION ${MIME_DIR}/packages)
+install (FILES pixmaps/petri-foo.png DESTINATION ${PIXMAPS_DIR})
+install (FILES petri-foo.desktop DESTINATION ${APPLICATIONS_DIR})
+
+if (UpdateMime)
+    install (CODE
+        "EXEC_PROGRAM ( \"update-mime-database ${CMAKE_INSTALL_PREFIX}/${MIME_DIR}\" ) "
+    )
+endif (UpdateMime)
+
+
diff --git a/ChangeLog b/ChangeLog
index ac423fb..da1726e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,115 @@
+2011-08-06 Brendan Jones <brendan.jones.it at gmail.com>
+
+	* gui/global_settings.[ch]
+	  Stores last-used sample and bank directories into
+	  XDG_CONFIG_HOME/petri-foo/rc.xml
+
+
+2011-08-06 James Morris <james at jwm-art.net>
+
+	* New zoom-in, zoom-out, zoom-fit, and zoom-1:1 buttons
+	  these buttons replace the cruddy oldspin button previously
+	  used for zooming and which the values of were meaningless.
+
+	* Mouse-wheel zooming.
+	  Moving the mouse-wheel down will zoom in to wherever the
+	  pointer is pointing in the waveform.
+
+	* Probably stuff which I've forgotten and not added into
+	  this Changelog.
+
+	* PHIN fan sliders have fans disabled by default.
+	  the fans of the fan sliders require desktop compositing
+	  so are disabled by default.
+
+	* PHIN fan slider setting stored via global_settings
+
+	* sample.c: Fixed raw/headerless sample loading
+
+	* float_section.c: Fixed portamento time setting.
+
+
+2011-07-26 James Morris <james at jwm-art.net>
+
+	* new build system using cmake. alter dir structures.
+
+	* include subset of PHAT in build and call it phin.
+
+
+2011-07-23 James Morris <james at jwm-art.net>
+
+	* patch_util.c, new function: patch_get_samplerate(void)
+
+	* dish_file.c: current samplerate is written into the
+	  dish_file within the "Master" node. This means that if the
+	  bank is loaded at a later date and JACK is running at a
+	  different samplerate, the values for the play+loop points
+	  can be adjusted accordingly for the resampled audio-data.
+
+	* sample.[ch]: removed resample_ratio from struct _Sample.
+	  the resample_ratio member was added in the previous commit
+	  and removed again due to the dish_file being responsible
+	  for calculating differences in play+loop points when
+	  JACK samplerate changes (between instances, not during a
+	  single instance).
+
+	* sample.c, sample_default: scaled output of LFO.
+	  scale output of LFO to prevent the default sample having
+	  samples exceeding the range -1.0 to +1.0.
+
+
+2011-07-20 James Morris <james at jwm-art.net>
+
+	* dish_file.c: fix/restore read/write of Portamento/Legato
+
+	* sample.[ch]: added sample_get_resampled_size
+	  this function provides a means to get the size the
+	  sample will use once loaded. this is required for when
+	  soft-limits to the size of samples petri-foo will load
+	  is implemented.
+
+	* sample.[ch]: addition and usage of MAX_SAMPLE_FRAMES
+	  enumeration MAX_SAMPLE_FRAMES = INT32_MAX; replaces
+	  previous detection of overly-large samples by casting
+	  sf_count_t variables to int and noticing difference.
+
+	* patch_util.c, patch_sample_load: scale loop points.
+	  loop points of the default sample are now scaled to
+	  the global sample rate as they should.
+
+
+2011-07-19 James Morris <james at jwm-art.net>
+
+	* Minimum and maximum Velocity value specification
+	  for patche to allow velocity layering of patches.
+	  Thanks to Brendan Jones for submitting patch.
+
+	* mod_section.c: fix bug in amplitude modulation amount
+	  Finally fixed this bug by the removal of a dangerously
+	  misplaced else.
+
+	* --unconnected command line option to not auto-connect
+	  JACK ports. This is required for JACK Session to work
+	  properly.
+
+	* patch.c, patch_set_and_get.c, mod_section.c:
+					inverted velocity mapping
+	  Allows negative setting of velocity sensitivity. Means
+	  you can cause low velocity to have the effect of high
+	  velocity and vice-versa.
+
+
+2011-07-14 James Morris <james at jwm-art.net>
+
+	* Auto preview toggle added to the sample selector
+	  dialog so that samples automatically play without
+	  loading.
+
+	* Added keyboard shortcut for 'Loa_d' in sample selector.
+
+Thanks to Brendan Jones for submitting these additions.
+
+
 2011-06-30  James Morris <james at jwm-art.net>
 
 	* added gpl header to all source files
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 54caf7c..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,229 +0,0 @@
-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
-Foundation, Inc.
-
-   This file is free documentation; the Free Software Foundation gives
-unlimited permission to copy, distribute and modify it.
-
-Basic Installation
-==================
-
-   These are generic installation instructions.
-
-   The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation.  It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions.  Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, and a
-file `config.log' containing compiler output (useful mainly for
-debugging `configure').
-
-   It can also use an optional file (typically called `config.cache'
-and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring.  (Caching is
-disabled by default to prevent problems with accidental use of stale
-cache files.)
-
-   If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release.  If you are using the cache, and at
-some point `config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
-   The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'.  You only need
-`configure.ac' if you want to change it or regenerate `configure' using
-a newer version of `autoconf'.
-
-The simplest way to compile this package is:
-
-  1. `cd' to the directory containing the package's source code and type
-     `./configure' to configure the package for your system.  If you're
-     using `csh' on an old version of System V, you might need to type
-     `sh ./configure' instead to prevent `csh' from trying to execute
-     `configure' itself.
-
-     Running `configure' takes awhile.  While running, it prints some
-     messages telling which features it is checking for.
-
-  2. Type `make' to compile the package.
-
-  3. Optionally, type `make check' to run any self-tests that come with
-     the package.
-
-  4. Type `make install' to install the programs and any data files and
-     documentation.
-
-  5. You can remove the program binaries and object files from the
-     source code directory by typing `make clean'.  To also remove the
-     files that `configure' created (so you can compile the package for
-     a different kind of computer), type `make distclean'.  There is
-     also a `make maintainer-clean' target, but that is intended mainly
-     for the package's developers.  If you use it, you may have to get
-     all sorts of other programs in order to regenerate files that came
-     with the distribution.
-
-Compilers and Options
-=====================
-
-   Some systems require unusual options for compilation or linking that
-the `configure' script does not know about.  Run `./configure --help'
-for details on some of the pertinent environment variables.
-
-   You can give `configure' initial values for configuration parameters
-by setting variables in the command line or in the environment.  Here
-is an example:
-
-     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
-
-   *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
-   You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory.  To do this, you must use a version of `make' that
-supports the `VPATH' variable, such as GNU `make'.  `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script.  `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
-
-   If you have to use a `make' that does not support the `VPATH'
-variable, you have to compile the package for one architecture at a
-time in the source code directory.  After you have installed the
-package for one architecture, use `make distclean' before reconfiguring
-for another architecture.
-
-Installation Names
-==================
-
-   By default, `make install' will install the package's files in
-`/usr/local/bin', `/usr/local/man', etc.  You can specify an
-installation prefix other than `/usr/local' by giving `configure' the
-option `--prefix=PATH'.
-
-   You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files.  If you
-give `configure' the option `--exec-prefix=PATH', the package will use
-PATH as the prefix for installing programs and libraries.
-Documentation and other data files will still use the regular prefix.
-
-   In addition, if you use an unusual directory layout you can give
-options like `--bindir=PATH' to specify different values for particular
-kinds of files.  Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
-
-   If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-Optional Features
-=================
-
-   Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System).  The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
-   For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-Specifying the System Type
-==========================
-
-   There may be some features `configure' cannot figure out
-automatically, but needs to determine by the type of machine the package
-will run on.  Usually, assuming the package is built to be run on the
-_same_ architectures, `configure' can figure that out, but if it prints
-a message saying it cannot guess the machine type, give it the
-`--build=TYPE' option.  TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name which has the form:
-
-     CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
-     OS KERNEL-OS
-
-   See the file `config.sub' for the possible values of each field.  If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
-   If you are _building_ compiler tools for cross-compiling, you should
-use the `--target=TYPE' option to select the type of system they will
-produce code for.
-
-   If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with `--host=TYPE'.
-
-Sharing Defaults
-================
-
-   If you want to set default values for `configure' scripts to share,
-you can create a site shell script called `config.site' that gives
-default values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists.  Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Defining Variables
-==================
-
-   Variables not defined in a site shell script can be set in the
-environment passed to `configure'.  However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost.  In order to avoid this problem, you should set
-them in the `configure' command line, using `VAR=value'.  For example:
-
-     ./configure CC=/usr/local2/bin/gcc
-
-will cause the specified gcc to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-`configure' Invocation
-======================
-
-   `configure' recognizes the following options to control how it
-operates.
-
-`--help'
-`-h'
-     Print a summary of the options to `configure', and exit.
-
-`--version'
-`-V'
-     Print the version of Autoconf used to generate the `configure'
-     script, and exit.
-
-`--cache-file=FILE'
-     Enable the cache: use and save the results of the tests in FILE,
-     traditionally `config.cache'.  FILE defaults to `/dev/null' to
-     disable caching.
-
-`--config-cache'
-`-C'
-     Alias for `--cache-file=config.cache'.
-
-`--quiet'
-`--silent'
-`-q'
-     Do not print messages saying which checks are being made.  To
-     suppress all normal output, redirect it to `/dev/null' (any error
-     messages will still be shown).
-
-`--srcdir=DIR'
-     Look for the package's source code in directory DIR.  Usually
-     `configure' can determine that directory automatically.
-
-`configure' also accepts some other, not widely useful, options.  Run
-`configure --help' for more details.
-
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 35b0918..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,3 +0,0 @@
-SUBDIRS = src pixmaps
-
-EXTRA_DIST = acx_pthread.m4 petri-foo.spec bootstrap BUGS ROADMAP WISHLIST
diff --git a/NEWS b/NEWS
index e69de29..cf9f12e 100644
--- a/NEWS
+++ b/NEWS
@@ -0,0 +1 @@
+See the Changelog and/or mailing list.
diff --git a/README b/README
index 2ee8752..9eabe88 100644
--- a/README
+++ b/README
@@ -5,75 +5,37 @@ Petri-Foo is a fork of the Specimen Sampler project. Specimen was
 originally developed by Pete Bessman. See the AUTHORS file for
 the list of other authors who've made contributions.
 
-Petri-Foo is forked and developed by James Morris.
-
-
-STAGE I GOALS
--------------
-
-The primary goal of forking Specimen to create Petri-Foo was to allow 
-frequency modulation of the LFOs, and to allow user selection of 
-modulation sources throughout the program.
-
-This has been accomplished.
-
-
-STAGE II GOALS
---------------
-
-Once stage 1 was complete, these were ideas I originally planned upon 
-implementing:
-
-  * Sample position retrigger by LFO.  TODO.
-  * Sample start position modulation.  TODO.
-  * Sample loop position modulation.   TODO.
-  * X-fades for loops/retriggers.      Basic implementation..
-
-  * Replace existing filter implementation. TODO.
-  * Visual display of ADSRs and LFOs.       FORGOTTEN.
-  * Noise modulation source.                TODO.
-  * Sample and Hold.                        TODO.
-
-  * Effects via LADSPA/LV2                  UNNECESSARY
-
-
-WHAT HAPPENED INSTEAD
----------------------
-
-  * Removal of deprecated GTK/GDK code
-  * Default auto-generated patch/sample
-  * Many code re-arrangements
-  * Mark navigation in waveform display
-  * Opening of raw audio formats
-  * Complete rewrite of bank file I/O code and XML file layout.
-  * MIDI Controllers subsumed as modulation sources.
-  * Amplitude modulation added to LFOs.
-
-
-STAGE III GOALS
----------------
-
-  * Obtain a stable feature set
-  * Sort out value mapping of MIDI controllers
-  * Figure out how to best handle state/switchable items that are
-    often controlled by MIDI controllers (ie portamento on/off)
-  + Sample point modulation as listed in stage 2 goals
-  + Figure out how to best handle things that are triggered
-  - Random/noise source
-  - Sample and hold
-   
-
-DEVELOPMENT
------------
-
-Development can be followed at http://github.com/jwm-art-net/Petri-Foo
-
-
-
-WHY HAVE I FORKED SPECIMEN?
----------------------------
-
-1) I think my changes are too much of a departure.
-2) Development of Specimen was dead.
-3) I had ideas for which I wanted the freedom to follow.
-
+Petri-Foo is forked and developed by James Morris (Feb 2011 +),
+with recent code contributions by Brendan Jones (July 2011 +).
+
+
+Differences, additions and improvements over Specimen
+------------------------------------------------------
+
+  * Default patch with saw-wave sample.
+  * Raw/Headerless sample file loading.
+  * JACK session support.
+  * Auto-Preview samples within the file selector.
+  * User-interface updated to contemporary GTK2 standards.
+  * Improved waveform rendering.
+  * Improved visual indication of play and loop selections.
+  * Play and loop point navigation.
+  * Improved waveform zooming, and addition of mouse-wheel zooming.
+  * Fading and X-Fading of sample.
+  * Improved MIDI CC handling.
+  * User-configurable modulation routing of ADSR, LFO, and MIDI CC.
+  * Keyboard tracking, and inverted keyboard tracking.
+  * Overall ADSR time scalable by keyboard tracking.
+  * Per-patch velocity range.
+  * Invertible velocity sensing.
+  * Amplitude modulation of LFO output.
+  * Removal of deprecated code, and customized PHAT library
+    for an improved life-span.
+  * Last-used directory recall.
+  * Shift/Control + Left-Mouse-Click for increased slider precision.
+  * JACK Audio output only, ALSA audio output removed[2].
+  * Removed LASH support.
+
+
+Further information about Petri-Foo can always be found at:
+http://petri-foo.sourceforge.net
diff --git a/ROADMAP b/ROADMAP
deleted file mode 100644
index de9f38f..0000000
--- a/ROADMAP
+++ /dev/null
@@ -1,15 +0,0 @@
-strip out old build system
-add new build system from  peter <zenadsl6252 at zen.co.uk>
-  make it work with just specimen devel
-merge jack midi
-  test build
-  test functionality
-merge lash patch
-  test build
-  test functionality
-jack_client_new vs _open change
-release 0.5.2rcN
-  fix bugs
-    repeat
-release 0.5.2
-
diff --git a/TODO b/TODO
index 2a65517..261a3ad 100644
--- a/TODO
+++ b/TODO
@@ -1,16 +1,4 @@
-
-***********************************************************************
--Engine: specimen can have portamento turned on/off via midi cc,
-
-    petri-foo is not currently capable of such a thing.
-    implement method of doing so, giving user same range of
-    choice as existing modulation slots.
-
-    the method should be capable to be used by other parameters
-    such as playback mode, sample positions, monophonic + legato
-    operation
-***********************************************************************
-
+-Engine: Mono/Poly mode switching via midi-cc (see notes on petri-foo-devel)
 
 -Engine: Voice modes
     +polyphonic re-trigger mode (new note repetition cuts old note).
@@ -20,6 +8,7 @@
 
 -Engine: Velocity sensitivity
     +add to eg's for amplitude modulation (i'm not convinced by this)
+     (note this has been coded but was commented out).
 
 -Engine: Logarithmic Amplitudes
     +Fix and remove artefacts arising from use of LAT for log_amplitude
@@ -29,6 +18,10 @@
 -GUI: general
     +replace PHAT widgets with something else?
     +custom cairo based widgets?
+     Petri-Foo now uses Phin instead of PHAT. Phin is a modified
+     fork of PHAT which uses Cairo and avoids deprecated GTK/GDK code,
+     with the fans on the sliders disabled but default by allowed to
+     be enabled by users of compositing desktop environments.
 
 
 -GUI: Patch List:
diff --git a/TODO.specimen b/TODO.specimen
index 0d00347..acb2fec 100644
--- a/TODO.specimen
+++ b/TODO.specimen
@@ -1,4 +1,4 @@
-(TODO file from the Specimen project)
+(TODO file from the Specimen project, edited: 19/07/2011)
 
 -Add per-channel volume and panning parameters
 
@@ -11,15 +11,8 @@
 
 -Make "loop to end" optional
 
--Create a better file format
-
--Make a sample browser
-
 -Allow patches to have their own JACK ports
 
--Automatically append .beef to a filename when saving a bank if it
- doesn't have that already
-
 -Make it possible to copy and paste whole parameter tabs
 
 -A way to import a bunch of samples at once
@@ -27,18 +20,10 @@
 -TOOLTIPS!  Or a status bar... or tooltips AND a statusbar!  This is
  low hanging fruit, any takers?  Please?  FOR THE LOVE OF GOD!!!
 
--Make the bank save-as dialog prompt you before overwriting
-an existing file
-
--Allow patches to host a few effects
-
 -Allow for longer times in the Envelopes and LFOs using non-linear sliders
 
 -Add more filter types
 
--Figure out why some parts of the gui don't redraw properly, such as
-the waveform and the audio-settings window
-
 -Add a progressbar when loading soundfiles and banks
 
 -Fix syncing drift
@@ -66,4 +51,3 @@ the waveform and the audio-settings window
 
 -Add a sampling capability
 
--Fix --enable-debug, which currently does nothing
diff --git a/WISHLIST b/WISHLIST
index 6126ae5..6f104e5 100644
--- a/WISHLIST
+++ b/WISHLIST
@@ -1,6 +1,3 @@
-lash -everyone
-jack-midi -everyone
-
 multiple jack outs -thorwil, yves potin
 envelope, velocity controllable plugins -thorwil, larsl
 
diff --git a/acx_pthread.m4 b/acx_pthread.m4
deleted file mode 100644
index 27079de..0000000
--- a/acx_pthread.m4
+++ /dev/null
@@ -1,199 +0,0 @@
-dnl Available from the GNU Autoconf Macro Archive at:
-dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html
-dnl
-AC_DEFUN([ACX_PTHREAD], [
-AC_REQUIRE([AC_CANONICAL_HOST])
-AC_LANG_SAVE
-AC_LANG_C
-acx_pthread_ok=no
-
-# We used to check for pthread.h first, but this fails if pthread.h
-# requires special compiler flags (e.g. on True64 or Sequent).
-# It gets checked for in the link test anyway.
-
-# First of all, check if the user has set any of the PTHREAD_LIBS,
-# etcetera environment variables, and if threads linking works using
-# them:
-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
-        save_CFLAGS="$CFLAGS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-        save_LIBS="$LIBS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
-        AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
-        AC_MSG_RESULT($acx_pthread_ok)
-        if test x"$acx_pthread_ok" = xno; then
-                PTHREAD_LIBS=""
-                PTHREAD_CFLAGS=""
-        fi
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
-fi
-
-# We must check for the threads library under a number of different
-# names; the ordering is very important because some systems
-# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
-# libraries is broken (non-POSIX).
-
-# Create a list of thread flags to try.  Items starting with a "-" are
-# C compiler flags, and other items are library names, except for "none"
-# which indicates that we try without any flags at all, and "pthread-config"
-# which is a program returning the flags for the Pth emulation library.
-
-acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
-
-# The ordering *is* (sometimes) important.  Some notes on the
-# individual items follow:
-
-# pthreads: AIX (must check this before -lpthread)
-# none: in case threads are in libc; should be tried before -Kthread and
-#       other compiler flags to prevent continual compiler warnings
-# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
-# -pthreads: Solaris/gcc
-# -mthreads: Mingw32/gcc, Lynx/gcc
-# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
-#      doesn't hurt to check since this sometimes defines pthreads too;
-#      also defines -D_REENTRANT)
-# pthread: Linux, etcetera
-# --thread-safe: KAI C++
-# pthread-config: use pthread-config program (for GNU Pth library)
-
-case "${host_cpu}-${host_os}" in
-        *solaris*)
-
-        # On Solaris (at least, for some versions), libc contains stubbed
-        # (non-functional) versions of the pthreads routines, so link-based
-        # tests will erroneously succeed.  (We need to link with -pthread or
-        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
-        # a function called by this macro, so we could check for that, but
-        # who knows whether they'll stub that too in a future libc.)  So,
-        # we'll just look for -pthreads and -lpthread first:
-
-        acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
-        ;;
-esac
-
-if test x"$acx_pthread_ok" = xno; then
-for flag in $acx_pthread_flags; do
-
-        case $flag in
-                none)
-                AC_MSG_CHECKING([whether pthreads work without any flags])
-                ;;
-
-                -*)
-                AC_MSG_CHECKING([whether pthreads work with $flag])
-                PTHREAD_CFLAGS="$flag"
-                ;;
-
-		pthread-config)
-		AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
-		if test x"$acx_pthread_config" = xno; then continue; fi
-		PTHREAD_CFLAGS="`pthread-config --cflags`"
-		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
-		;;
-
-                *)
-                AC_MSG_CHECKING([for the pthreads library -l$flag])
-                PTHREAD_LIBS="-l$flag"
-                ;;
-        esac
-
-        save_LIBS="$LIBS"
-        save_CFLAGS="$CFLAGS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
-        # Check for various functions.  We must include pthread.h,
-        # since some functions may be macros.  (On the Sequent, we
-        # need a special flag -Kthread to make this header compile.)
-        # We check for pthread_join because it is in -lpthread on IRIX
-        # while pthread_create is in libc.  We check for pthread_attr_init
-        # due to DEC craziness with -lpthreads.  We check for
-        # pthread_cleanup_push because it is one of the few pthread
-        # functions on Solaris that doesn't have a non-functional libc stub.
-        # We try pthread_create on general principles.
-        AC_TRY_LINK([#include <pthread.h>],
-                    [pthread_t th; pthread_join(th, 0);
-                     pthread_attr_init(0); pthread_cleanup_push(0, 0);
-                     pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
-                    [acx_pthread_ok=yes])
-
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
-
-        AC_MSG_RESULT($acx_pthread_ok)
-        if test "x$acx_pthread_ok" = xyes; then
-                break;
-        fi
-
-        PTHREAD_LIBS=""
-        PTHREAD_CFLAGS=""
-done
-fi
-
-# Various other checks:
-if test "x$acx_pthread_ok" = xyes; then
-        save_LIBS="$LIBS"
-        LIBS="$PTHREAD_LIBS $LIBS"
-        save_CFLAGS="$CFLAGS"
-        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
-
-        # Detect AIX lossage: threads are created detached by default
-        # and the JOINABLE attribute has a nonstandard name (UNDETACHED).
-        AC_MSG_CHECKING([for joinable pthread attribute])
-        AC_TRY_LINK([#include <pthread.h>],
-                    [int attr=PTHREAD_CREATE_JOINABLE;],
-                    ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
-        if test x"$ok" = xunknown; then
-                AC_TRY_LINK([#include <pthread.h>],
-                            [int attr=PTHREAD_CREATE_UNDETACHED;],
-                            ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
-        fi
-        if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
-                AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok,
-                          [Define to the necessary symbol if this constant
-                           uses a non-standard name on your system.])
-        fi
-        AC_MSG_RESULT(${ok})
-        if test x"$ok" = xunknown; then
-                AC_MSG_WARN([we do not know how to create joinable pthreads])
-        fi
-
-        AC_MSG_CHECKING([if more special flags are required for pthreads])
-        flag=no
-        case "${host_cpu}-${host_os}" in
-                *-aix* | *-freebsd*)     flag="-D_THREAD_SAFE";;
-                *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
-        esac
-        AC_MSG_RESULT(${flag})
-        if test "x$flag" != xno; then
-                PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
-        fi
-
-        LIBS="$save_LIBS"
-        CFLAGS="$save_CFLAGS"
-
-        # More AIX lossage: must compile with cc_r
-        AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
-else
-        PTHREAD_CC="$CC"
-fi
-
-AC_SUBST(PTHREAD_LIBS)
-AC_SUBST(PTHREAD_CFLAGS)
-AC_SUBST(PTHREAD_CC)
-
-# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
-if test x"$acx_pthread_ok" = xyes; then
-        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
-        :
-else
-        acx_pthread_ok=no
-        $2
-fi
-AC_LANG_RESTORE
-])dnl ACX_PTHREAD
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index c1be260..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-set -x
-
-aclocal -I .
-autoheader
-autoconf -f
-automake -a
-
diff --git a/bootstrap b/bootstrap
deleted file mode 100755
index c1be260..0000000
--- a/bootstrap
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-set -x
-
-aclocal -I .
-autoheader
-autoconf -f
-automake -a
-
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..0e184a4
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,11 @@
+#define PACKAGE     "${PROJECT_NAME}"
+#define VERSION \
+    "0.${Petri-Foo_VERSION_MAJOR}.${Petri-Foo_VERSION_MINOR}"
+
+#ifndef PIXMAPS_DIR
+#define PIXMAPS_DIR "${CMAKE_INSTALL_PREFIX}/${DATADIR}/pixmaps"
+#endif
+
+#cmakedefine HAVE_MALLOC_H 1
+#cmakedefine HAVE_JACK_SESSION_H 1
+#cmakedefine DEBUG 1
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index 4c7d6cc..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,200 +0,0 @@
-# -*- autoconf -*-
-# Process this file with autoconf to produce a configure script.
-
-AC_INIT(configure.ac)
-AM_INIT_AUTOMAKE(petri-foo, 0.0.2)
-AM_CONFIG_HEADER(src/config.h)
-
-# compilation
-with_debug="no"
-AC_ARG_ENABLE([debug], 
-    [AC_HELP_STRING([--enable-debug],
-        [enable debugging information, accepting a performance penalty (default is NO)])],
-    [if test x$enable_debug = xyes; then with_debug=yes ; fi])
-
-if test x$with_debug = xno; then
-    CFLAGS="-O3 -std=gnu99"
-else
-    CFLAGS="-O0 -std=gnu99 -ggdb -Wextra -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGSEAL_ENABLE"
-    AC_DEFINE(DEBUG, 1, [[whether to display debugging output or not]])
-fi
-
-
-# standard autoconf checks
-AC_PROG_CC
-AM_PROG_CC_C_O
-AC_PROG_CPP
-AC_PROG_RANLIB
-AC_HEADER_STDC
-AC_CHECK_HEADERS([stdlib.h string.h sys/time.h unistd.h])
-AC_C_CONST
-AC_C_INLINE
-AC_HEADER_TIME
-AC_FUNC_MALLOC
-AC_CHECK_FUNCS([floor gettimeofday pow strchr strdup])
-
-# pthreads
-ACX_PTHREAD
-
-PETRI_FOO_CFLAGS="-Wall"
-AC_SUBST(PETRI_FOO_CFLAGS)
-CFLAGS="$PETRI_FOO_CFLAGS $CFLAGS"
-
-config_error="no"
-
-# gtk
-PKG_CHECK_MODULES(GTK, gtk+-2.0, HAVE_GTK="yes", config_error="yes")
-AC_SUBST(GTK_CFLAGS)
-AC_SUBST(GTK_LIBS)
-
-# libxml
-PKG_CHECK_MODULES(LIBXML, libxml-2.0, HAVE_LIBXML="yes", config_error="yes")
-AC_SUBST(LIBXML_CFLAGS)
-AC_SUBST(LIBXML_LIBS)
-
-# libgnomecanvas
-PKG_CHECK_MODULES(LIBGNOMECANVAS, libgnomecanvas-2.0, HAVE_LIBGNOMECANVAS="yes", config_error="yes")
-AC_SUBST(LIBGNOMECANVAS_CFLAGS)
-AC_SUBST(LIBGNOMECANVAS_LIBS)
-
-# alsa
-PKG_CHECK_MODULES(ALSA, alsa, HAVE_ALSA="yes", config_error="yes")
-AC_SUBST(ALSA_CFLAGS)
-AC_SUBST(ALSA_LIBS)
-
-# libsndfile
-PKG_CHECK_MODULES(LIBSNDFILE, sndfile, HAVE_LIBSNDFILE="yes", config_error="yes")
-AC_SUBST(LIBSNDFILE_CFLAGS)
-AC_SUBST(LIBSNDFILE_LIBS)
-
-# libsamplerate
-PKG_CHECK_MODULES(LIBSAMPLERATE, samplerate, HAVE_LIBSAMPLERATE="yes", config_error="yes")
-AC_SUBST(LIBSAMPLERATE_CFLAGS)
-AC_SUBST(LIBSAMPLERATE_LIBS)
-
-# jack
-PKG_CHECK_MODULES(JACK, jack >= 0.116.0, HAVE_JACK="yes", config_error="yes")
-if test "x$HAVE_JACK" = xyes ; then
-  AC_CHECK_HEADER( jack/session.h, HAVE_JACK_SESSION="yes", HAVE_JACK_SESSION="no" )
-  if test "x$HAVE_JACK_SESSION" = xyes; then
-    AC_DEFINE([HAVE_JACK_SESSION], [], [Define if we have jack session support.])
-  fi
-fi
-AC_SUBST(JACK_CFLAGS)
-AC_SUBST(JACK_LIBS)
-
-# phat
-PKG_CHECK_MODULES(PHAT, phat >= 0.3.1, HAVE_PHAT="yes", config_error="yes")
-AC_SUBST(PHAT_CFLAGS)
-AC_SUBST(PHAT_LIBS)
-
-
-CC="$PTHREAD_CC"
-
-# print build summary
-AC_CONFIG_COMMANDS_POST([
-echo
-echo    "                   BUILD SUMMARY"
-echo    "                   ============="
-echo    "    Compiler full flags: $CFLAGS"
-echo -n "             Build type: "
-if test x$with_debug = xyes; then
-    echo "debugging"
-else
-    echo "optimized"
-fi
-echo -n "      Jack midi support: "
-if test "x$HAVE_JACK" = xyes ; then
-    echo "yes with jack >= 0.116.0"
-fi
-echo -n "   Jack session support: "
-if test "x$HAVE_JACK_SESSION" = xyes ; then
-    echo "yes"
-else
-    echo "not found"
-fi
-
-echo
-echo
-])
-
-# are we good to go?
-
-if test x$config_error = "xyes"; then
-
-AC_MSG_RESULT([
-*** ERROR: the following required packages are missing ***
-])
-
-if test "x$HAVE_GTK" != xyes ; then
-  AC_MSG_RESULT([
-*** GTK+ version 2.2.x or greater, available from :
-		http://www.gtk.org/
-])
-fi
-
-if test "x$HAVE_LIBGNOMECANVAS" != xyes ; then
-  AC_MSG_RESULT([
-*** libgnomecanvas, available from :
-		http://ftp.gnome.org/pub/gnome/sources/libgnomecanvas/
-])
-fi
-
-if test "x$HAVE_PHAT" != xyes ; then
-  AC_MSG_RESULT([
-*** Phat 0.4.0 or greater, available from:
-		http://phat.berlios.de/
-])
-fi
-
-if test "x$HAVE_JACK" != xyes ; then
-  AC_MSG_RESULT([
-*** JACK 0.99.0 or greater, available from:
-		http://www.jackaudio.org/
-])
-fi
-
-if test "x$HAVE_LIBSAMPLERATE" != xyes ; then
-  AC_MSG_RESULT([
-*** libsamplerate, available from:
-		http://www.mega-nerd.com/SRC/
-])
-fi
-
-if test "x$HAVE_LIBSNDFILE" != xyes ; then
-  AC_MSG_RESULT([
-*** libsndfile, available from:
-		http://www.mega-nerd.com/libsndfile/
-])
-fi
-
-if test "x$HAVE_LIBXML" != xyes ; then
-  AC_MSG_RESULT([
-*** libxml2, available from:
-		http://ftp.gnome.org/pub/gnome/sources/libxml2/
-])
-fi
-
-
-AC_MSG_RESULT([
-Please ensure that all the above software is properly installed
-before running configure again. To do this, use your package
-manager to install the correct versions of the binary AND 
-development (dev) packages or, download them from the 
-above link(s) and build and install them from source manually.
-])
-
-AC_MSG_ERROR([
-*************************************************
-see ./config.log for full details.
-])
-else
-AC_OUTPUT([
-Makefile
-pixmaps/Makefile
-src/Makefile
-src/gui/Makefile
-src/patch_private/Makefile
-petri-foo.spec
-])
-fi
diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt
new file mode 100644
index 0000000..803a142
--- /dev/null
+++ b/gui/CMakeLists.txt
@@ -0,0 +1,25 @@
+file (GLOB PETRI_FOO_SOURCES *.c)
+
+include_directories (
+    ${Petri-Foo_SOURCE_DIR}/libpetrifoo
+    ${Petri-Foo_SOURCE_DIR}/libpetrifui
+    ${Petri-Foo_SOURCE_DIR}/libphin
+    ${GTK2_INCLUDE_DIRS}
+    ${LIBGNOMECANVAS2_INCLUDE_DIRS}
+    ${LIBXML2_INCLUDE_DIRS}
+    )
+
+link_directories (
+    ${GTK2_LIBRARY_DIRS}
+    )
+
+add_definitions (
+    ${GTK2_CFLAGS_OTHER}
+    )
+
+add_executable(petri-foo ${PETRI_FOO_SOURCES})
+
+
+target_link_Libraries(petri-foo petrifoo petrifui pthread phin )
+
+install (TARGETS petri-foo DESTINATION ${BINDIR})
diff --git a/src/gui/audio-settings.c b/gui/audio-settings.c
similarity index 98%
rename from src/gui/audio-settings.c
rename to gui/audio-settings.c
index e111df3..8a0ce12 100644
--- a/src/gui/audio-settings.c
+++ b/gui/audio-settings.c
@@ -38,7 +38,7 @@
 static GtkWidget* window;
 
 
-#ifdef HAVE_JACK_SESSION
+#ifdef HAVE_JACK_SESSION_H
 static gboolean gui_session_cb(void *data)
 {
     size_t len;
@@ -62,7 +62,7 @@ static gboolean gui_session_cb(void *data)
     debug("filename:%s\n",filename);
 
     snprintf(command,   sizeof(command),
-                        "petri-foo -U %s ${SESSION_DIR}%s",
+                        "petri-foo --unconnected -U %s ${SESSION_DIR}%s",
                         ev->client_uuid, bankfilename);
 
     debug("command:%s\n",command);
diff --git a/src/gui/audio-settings.h b/gui/audio-settings.h
similarity index 95%
rename from src/gui/audio-settings.h
rename to gui/audio-settings.h
index 8aa9ae8..9a9a784 100644
--- a/src/gui/audio-settings.h
+++ b/gui/audio-settings.h
@@ -29,7 +29,7 @@
 
 #include "config.h"
 
-#ifdef HAVE_JACK_SESSION
+#ifdef HAVE_JACK_SESSION_H
 #include <jack/session.h>
 #endif
 
@@ -38,7 +38,7 @@ void audio_settings_init(GtkWidget* parent);
 void audio_settings_show(void);
 
 
-#ifdef HAVE_JACK_SESSION
+#ifdef HAVE_JACK_SESSION_H
 void audio_settings_session_cb(jack_session_event_t *event, void *arg);
 #endif
 
diff --git a/src/gui/bank-ops.c b/gui/bank-ops.c
similarity index 64%
rename from src/gui/bank-ops.c
rename to gui/bank-ops.c
index 0f850a2..8f488e6 100644
--- a/src/gui/bank-ops.c
+++ b/gui/bank-ops.c
@@ -28,10 +28,13 @@
 #include <string.h>
 
 #include "dish_file.h"
+#include "log_display.h"
 #include "patch.h"
 #include "patch_util.h"
 #include "petri-foo.h"
+#include "msg_log.h"
 #include "gui.h"
+#include "global_settings.h"
 
 
 static char *last_bank = 0;
@@ -55,15 +58,6 @@ static void set_bankname(const char* name)
     gui_set_window_title(bankname);
 }
 
-
-inline static char* strconcat(const char* str1, const char* str2)
-{
-    char* str = malloc(strlen(str1) + strlen(str2) + 1);
-    strcpy(str, str1);
-    strcat(str, str2);
-    return str;
-}
-
 /* unused... reason/purpose ???
 static char* get_file_filter(void)
 {
@@ -99,6 +93,8 @@ int bank_ops_save_as (GtkWidget* parent_window)
 
     char* filter = strconcat("*", dish_file_extension());
     char* untitled_dish = strconcat("untitled", dish_file_extension());
+    
+    global_settings* settings = settings_get();
 
     dialog = gtk_file_chooser_dialog_new("Save Bank",
                                     GTK_WINDOW(parent_window),
@@ -110,6 +106,13 @@ int bank_ops_save_as (GtkWidget* parent_window)
     gtk_file_chooser_set_do_overwrite_confirmation(
                                     GTK_FILE_CHOOSER(dialog), TRUE);
 
+    if ( last_bank == 0)   
+        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
+                                    settings->last_bank_dir);
+    else 
+        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
+                                    g_path_get_dirname(last_bank));
+ 
     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
                     (last_bank != 0) ? last_bank : untitled_dish);
 
@@ -123,7 +126,7 @@ int bank_ops_save_as (GtkWidget* parent_window)
 
         if ((val = dish_file_write(name)) < 0)
         {
-            errmsg ("Failed to write file %s\n", name);
+            msg_log(MSG_ERROR, "Failed to write file %s\n", name);
             GtkWidget* msg = gtk_message_dialog_new(GTK_WINDOW(dialog),
                                     GTK_DIALOG_MODAL,
                                     GTK_MESSAGE_ERROR,
@@ -136,7 +139,10 @@ int bank_ops_save_as (GtkWidget* parent_window)
         }
         else
         {
-            debug ("Succesfully wrote file %s\n", name);
+
+            gtk_recent_manager_add_item (recent_manager, 
+                g_filename_to_uri(name, NULL, NULL));
+            msg_log(MSG_MESSAGE, "Succesfully wrote file %s\n", name);
             free(last_bank);
             last_bank = strdup(name);
             set_bankname(name);
@@ -174,6 +180,7 @@ int bank_ops_open(GtkWidget* parent_window)
     GtkWidget* dialog;
     int val;
     char* filter = strconcat("*", dish_file_extension());
+    global_settings* settings = settings_get();
 
     dialog = gtk_file_chooser_dialog_new("Open Bank",
                                           GTK_WINDOW(parent_window),
@@ -182,26 +189,33 @@ int bank_ops_open(GtkWidget* parent_window)
                                           GTK_RESPONSE_CANCEL,
                                           GTK_STOCK_OPEN,
                                           GTK_RESPONSE_ACCEPT, NULL);
-    if (last_bank)
-        gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog),
+    if (last_bank) 
+       gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog),
                                                         last_bank);
+    else
+        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
+                                           settings->last_bank_dir);
 
     file_chooser_add_filter(dialog, "Petri-Foo files", filter);
     file_chooser_add_filter(dialog, "All files", "*");
 
     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
     {
-        char *name = (char *)
-            gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+        char* name = (char*) gtk_file_chooser_get_filename(
+                                        GTK_FILE_CHOOSER(dialog));
+
+        msg_log(MSG_MESSAGE, "Loading bank %s\n", name);
+        msg_log_reset_notification_state();
+        val = dish_file_read(name);
 
-        if ((val = dish_file_read(name)) < 0)
+        if (val < 0)
         {
-            errmsg("Failed to read file %s\n", name);
+            msg_log(MSG_ERROR, "Failed to read bank %s\n", name);
             GtkWidget* msg = gtk_message_dialog_new(GTK_WINDOW(dialog),
                                     GTK_DIALOG_MODAL,
                                     GTK_MESSAGE_ERROR,
                                     GTK_BUTTONS_CLOSE,
-                                    "Failed to read file %s\n.", name);
+                                    "Failed to read bank %s\n.", name);
 
             g_signal_connect_swapped(G_OBJECT(msg), "response",
                                     G_CALLBACK(gtk_widget_destroy), msg);
@@ -209,9 +223,23 @@ int bank_ops_open(GtkWidget* parent_window)
         }
         else
         {
-            debug ("Succesfully read file %s\n", name);
+            if (msg_log_get_notification_state())
+            {
+                msg_log(MSG_WARNING, "Bank %s read with errors\n", name);
+                log_display_show();
+            }
+            else
+                msg_log(MSG_MESSAGE, "Succesfully read bank %s\n", name);
+
+            gtk_recent_manager_add_item(recent_manager,
+                                    g_filename_to_uri(name, NULL, NULL));
             free(last_bank);
             last_bank = strdup(name);
+
+            if (settings->last_bank_dir)
+                free(settings->last_bank_dir);
+
+            settings->last_bank_dir = g_path_get_dirname(name);
             set_bankname(name);
         }
     }
@@ -236,3 +264,45 @@ int bank_ops_new(void)
     set_bankname(NULL);
     return 0;
 }
+
+int bank_ops_open_recent(GtkWidget* parent_window, char* filename)
+{
+    int val;
+
+    msg_log(MSG_MESSAGE, "Loading bank %s\n", filename);
+    msg_log_reset_notification_state();
+    val = dish_file_read(filename);
+
+    if (val < 0)
+    {
+         msg_log(MSG_ERROR, "Failed to read bank %s\n", filename);
+         GtkWidget* msg = gtk_message_dialog_new(GTK_WINDOW(parent_window),
+                                   GTK_DIALOG_MODAL,
+                                   GTK_MESSAGE_ERROR,
+                                   GTK_BUTTONS_CLOSE,
+                                   "Failed to read bank %s\n.", filename);
+
+         g_signal_connect_swapped(G_OBJECT(msg), "response",
+                                   G_CALLBACK(gtk_widget_destroy), msg);
+         gtk_widget_show (msg);
+    }
+    else
+    {
+        if (msg_log_get_notification_state())
+        {
+            msg_log(MSG_WARNING, "Bank %s read with errors\n", filename);
+            log_display_show();
+        }
+        else
+            msg_log(MSG_MESSAGE, "Succesfully read bank %s\n", filename);
+
+        gtk_recent_manager_add_item (recent_manager, 
+             g_filename_to_uri(filename, NULL, NULL));
+        free(last_bank);
+        last_bank = strdup(filename);
+        set_bankname(filename);
+    }
+    
+    return val;
+}
+
diff --git a/src/gui/bank-ops.h b/gui/bank-ops.h
similarity index 92%
rename from src/gui/bank-ops.h
rename to gui/bank-ops.h
index 760d2e0..ef388f7 100644
--- a/src/gui/bank-ops.h
+++ b/gui/bank-ops.h
@@ -34,5 +34,7 @@ int         bank_ops_open    (GtkWidget* parent_window);
 int         bank_ops_save_as (GtkWidget* parent_window);
 int         bank_ops_save    (GtkWidget* parent_window);
 const char* bank_ops_bank    (void);
+int         bank_ops_open_recent (GtkWidget* parent_window
+                , char* filename);
 
 #endif /* __BANK_OPS_H__ */
diff --git a/src/gui/basic_combos.c b/gui/basic_combos.c
similarity index 100%
rename from src/gui/basic_combos.c
rename to gui/basic_combos.c
diff --git a/src/gui/basic_combos.h b/gui/basic_combos.h
similarity index 100%
rename from src/gui/basic_combos.h
rename to gui/basic_combos.h
diff --git a/gui/bool_section.c b/gui/bool_section.c
new file mode 100644
index 0000000..0bad112
--- /dev/null
+++ b/gui/bool_section.c
@@ -0,0 +1,275 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <gtk/gtk.h>
+
+#include "phin.h"
+
+#include "bool_section.h"
+#include "gui.h"
+#include "patch_set_and_get.h"
+#include "names.h"
+
+#include "mod_src_gui.h"
+
+
+typedef struct _BoolSectionPrivate BoolSectionPrivate;
+
+#define BOOL_SECTION_GET_PRIVATE(obj)   \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+        BOOL_SECTION_TYPE, BoolSectionPrivate))
+
+struct _BoolSectionPrivate
+{
+    int             patch_id;
+    PatchBoolType   bool_type;
+
+    GtkWidget*      set_check;
+    GtkWidget*      mod_combo;
+    GtkWidget*      thresh;
+
+    GtkWidget*      mod_label;
+};
+
+/* signals */
+enum
+{
+    TOGGLED,
+    LAST_SIGNAL,
+};
+
+static int signals[LAST_SIGNAL];
+
+
+G_DEFINE_TYPE(BoolSection, bool_section, GTK_TYPE_VBOX);
+
+
+static void bool_section_class_init(BoolSectionClass* klass)
+{
+    GObjectClass* object_class = G_OBJECT_CLASS(klass);
+    bool_section_parent_class = g_type_class_peek_parent(klass);
+
+    signals[TOGGLED] =
+        g_signal_new   ("toggled",
+                        G_TYPE_FROM_CLASS(klass),
+                        G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                        G_STRUCT_OFFSET(BoolSectionClass, set_toggled),
+                        NULL, NULL,
+                        g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
+                        0, NULL);
+
+    g_type_class_add_private(object_class, sizeof(BoolSectionPrivate));
+}
+
+
+static void set_check_cb(GtkToggleButton* button, BoolSection* self)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+    int mod_src = mod_src_combo_get_mod_src_id(GTK_COMBO_BOX(p->mod_combo));
+    gboolean active = gtk_toggle_button_get_active(button);
+
+    gtk_widget_set_sensitive(p->mod_label,      active);
+    gtk_widget_set_sensitive(p->mod_combo,      active);
+    gtk_widget_set_sensitive(p->thresh, active && mod_src != MOD_SRC_NONE);
+
+    patch_bool_set_active(p->patch_id, p->bool_type, active);
+
+    g_signal_emit_by_name(G_OBJECT(self), "toggled");
+}
+
+
+static void mod_combo_cb(GtkComboBox* combo, BoolSectionPrivate* p)
+{
+    int mod_src = mod_src_combo_get_mod_src_id(combo);
+
+    patch_bool_set_mod_src( p->patch_id, p->bool_type, mod_src);
+
+    gtk_widget_set_sensitive(p->thresh, (mod_src != MOD_SRC_NONE));
+}
+
+
+static void thresh_cb(GtkWidget* w, BoolSectionPrivate* p)
+{
+    patch_bool_set_thresh(  p->patch_id,
+                            p->bool_type,
+                            phin_fan_slider_get_value(PHIN_FAN_SLIDER(w)));
+}
+
+
+static void block(BoolSectionPrivate* p)
+{
+    g_signal_handlers_block_by_func(p->set_check,   set_check_cb,   p);
+    g_signal_handlers_block_by_func(p->thresh,      thresh_cb,      p);
+    g_signal_handlers_block_by_func(p->mod_combo,   mod_combo_cb,   p);
+}
+
+
+static void unblock(BoolSectionPrivate* p)
+{
+    g_signal_handlers_unblock_by_func(p->set_check, set_check_cb,   p);
+    g_signal_handlers_unblock_by_func(p->thresh,    thresh_cb,      p);
+    g_signal_handlers_unblock_by_func(p->mod_combo, mod_combo_cb,   p);
+}
+
+
+
+static void bool_section_init(BoolSection* self)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+    p->patch_id = -1;
+    p->bool_type = PATCH_BOOL_INVALID;
+    p->set_check = 0;
+    p->mod_combo = 0;
+    p->thresh = 0;
+}
+
+
+void bool_section_set_bool( BoolSection* self, PatchBoolType bool_type)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+    GtkBox* box;
+    GtkWidget* table;
+    GtkTable* t;
+    GtkWidget* title;
+
+    int y = 0;
+
+    int a1 = 0, a2 = 1;
+    int b1 = 1, b2 = 2;
+    int c1 = 2, c2 = 3;
+
+    const char* bool_names[] = { "Portamento", "Mono", "Legato" };
+
+    box = GTK_BOX(self);
+
+    p->bool_type = bool_type;
+
+    if (bool_type < 0 || bool_type > 2)
+    {
+        debug("Bad News! unknown bool type!\n");
+        return;
+    }
+
+    gtk_container_set_border_width(GTK_CONTAINER(self), GUI_BORDERSPACE);
+
+    table = gtk_table_new(3, 3, FALSE);
+    t = (GtkTable*)table;
+    gui_pack(box, table);
+
+    title = gui_title_new(bool_names[bool_type]);
+    p->set_check = gtk_check_button_new();
+    gtk_container_add(GTK_CONTAINER(p->set_check), title);
+    gui_attach(t, p->set_check, a1, c2, y, y + 1);
+    gtk_widget_show(title);
+    ++y;
+
+    /* title padding */
+    gui_attach(t, gui_vpad_new(GUI_TITLESPACE), a1, c2, y, y + 1);
+    ++y;
+
+    /* label column spacing (of some description!?) */
+    gui_attach(t, gui_hpad_new(GUI_TEXTSPACE), c1, c2, y, y + 1);
+    ++y;
+
+    p->mod_label = gui_label_attach("Switch:", t, a1, a2, y, y + 1);
+
+    p->mod_combo = mod_src_new_combo_with_cell();
+    mod_src_combo_set_model(GTK_COMBO_BOX(p->mod_combo), MOD_SRC_GLOBALS);
+    gui_attach(t, p->mod_combo, b1, b2, y, y + 1);
+
+    p->thresh = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    gui_attach(t, p->thresh, c1, c2, y, y + 1);
+    gtk_widget_set_tooltip_text(p->thresh, "Switch threshold");
+    ++y;
+
+    g_signal_connect(G_OBJECT(p->set_check),       "toggled",
+                        G_CALLBACK(set_check_cb),      (gpointer)self);
+
+    g_signal_connect(G_OBJECT(p->thresh),           "value-changed",
+                        G_CALLBACK(thresh_cb),          (gpointer)p);
+
+    g_signal_connect(G_OBJECT(p->mod_combo),        "changed",
+                            G_CALLBACK(mod_combo_cb),   (gpointer)p);
+}
+
+
+void bool_section_set_list_global(BoolSection* self)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+
+    mod_src_combo_set_model(GTK_COMBO_BOX(p->mod_combo), MOD_SRC_GLOBALS);
+}
+
+
+void bool_section_set_list_all(BoolSection* self)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+
+    mod_src_combo_set_model(GTK_COMBO_BOX(p->mod_combo), MOD_SRC_ALL);
+}
+
+
+GtkWidget* bool_section_new(void)
+{
+    return (GtkWidget*)g_object_new(BOOL_SECTION_TYPE, NULL);
+}
+
+
+void bool_section_set_patch(BoolSection* self, int patch_id)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+
+    bool    set;
+    float   thresh;
+    int     modsrc;
+
+    GtkTreeIter moditer;
+
+    p->patch_id = patch_id;
+
+    if (patch_id < 0)
+        return;
+
+    patch_bool_get_all(patch_id, p->bool_type, &set, &thresh, &modsrc);
+
+    block(p);
+
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->thresh), thresh);
+
+    if (!mod_src_combo_get_iter_with_id(GTK_COMBO_BOX(p->mod_combo),
+                                        modsrc,
+                                        &moditer))
+    {
+        debug("failed to get mod source id from combo box\n");
+    }
+
+    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(p->mod_combo), &moditer);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->set_check), set);
+
+    unblock(p);
+}
+
+
+gboolean bool_section_get_active(BoolSection* self)
+{
+    BoolSectionPrivate* p = BOOL_SECTION_GET_PRIVATE(self);
+
+    return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->set_check));
+}
diff --git a/gui/bool_section.h b/gui/bool_section.h
new file mode 100644
index 0000000..4ac1d32
--- /dev/null
+++ b/gui/bool_section.h
@@ -0,0 +1,76 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or Boolify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef __BOOL_SECTION__
+#define __BOOL_SECTION__
+
+#include <gtk/gtk.h>
+
+
+#include "patch.h"
+
+
+G_BEGIN_DECLS
+
+#define BOOL_SECTION_TYPE       (bool_section_get_type())
+#define BOOL_SECTION(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                                BOOL_SECTION_TYPE, BoolSection))
+
+#define IS_BOOL_SECTION(obj)    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                                BOOL_SECTION_TYPE))
+
+#define BOOL_SECTION_CLASS(klass)(G_TYPE_CHECK_CLASS_CAST ((klass),  \
+                                BOOL_SECTION_TYPE, BoolSectionClass))
+
+#define IS_BOOL_SECTION_CLASS(klass) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((klass), BOOL_SECTION_TYPE))
+
+
+typedef struct _BoolSectionClass BoolSectionClass;
+typedef struct _BoolSection      BoolSection;
+
+
+struct _BoolSection
+{
+    GtkVBox parent_instance;
+};
+
+
+struct _BoolSectionClass
+{
+    GtkVBoxClass parent_class;
+     /* <private> */
+    void (*set_toggled)(BoolSection*);
+};
+
+
+GType       bool_section_get_type(void);
+
+GtkWidget*  bool_section_new(void);
+
+void        bool_section_set_bool( BoolSection*, PatchBoolType);
+void        bool_section_set_patch(BoolSection*, int patch_id);
+
+gboolean    bool_section_get_active(BoolSection*);
+
+G_END_DECLS
+
+
+#endif /* __BOOL_SECTION__ */
diff --git a/gui/channelsection.c b/gui/channelsection.c
new file mode 100644
index 0000000..ab6723e
--- /dev/null
+++ b/gui/channelsection.c
@@ -0,0 +1,191 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a Specimen original, modified 2011
+*/
+
+
+#include <gtk/gtk.h>
+
+#include "phin.h"
+
+#include "channelsection.h"
+#include "gui.h"
+#include "patchlist.h"
+#include "midi.h"
+#include "patch_set_and_get.h"
+
+
+
+G_DEFINE_TYPE(ChannelSection, channel_section, GTK_TYPE_VBOX)
+
+
+static void channel_section_class_init(ChannelSectionClass* klass)
+{
+    channel_section_parent_class = g_type_class_peek_parent(klass);
+}
+
+
+static void channel_cb(PhinSliderButton* button, ChannelSection* self)
+{
+    int channel = (int)phin_slider_button_get_value(button);
+    PatchList* list = gui_get_patch_list();
+
+    patch_set_channel(self->patch, channel-1);
+    patch_list_update(list, patch_list_get_current_patch(list),
+                                                PATCH_LIST_PATCH);
+}
+
+
+static void lower_vel_cb(PhinSliderButton* button, ChannelSection* self)
+{
+    int lower_vel = (int)phin_slider_button_get_value(button);
+    PatchList* list = gui_get_patch_list();
+
+    patch_set_lower_vel(self->patch, lower_vel);
+    patch_list_update(list, patch_list_get_current_patch(list),
+                                                PATCH_LIST_PATCH);
+}
+
+
+static void upper_vel_cb(PhinSliderButton* button, ChannelSection* self)
+{
+    int upper_vel = (int)phin_slider_button_get_value(button);
+    PatchList* list = gui_get_patch_list();
+
+    patch_set_upper_vel(self->patch, upper_vel);
+    patch_list_update(list, patch_list_get_current_patch(list),
+                                                PATCH_LIST_PATCH);
+}
+
+
+static void connect(ChannelSection* self)
+{
+    g_signal_connect(G_OBJECT(self->chan_sb), "value-changed",
+                        G_CALLBACK(channel_cb), (gpointer) self);
+    g_signal_connect(G_OBJECT(self->lower_vel_sb), "value-changed",
+                        G_CALLBACK(lower_vel_cb), (gpointer) self);
+    g_signal_connect(G_OBJECT(self->upper_vel_sb), "value-changed",
+                        G_CALLBACK(upper_vel_cb), (gpointer) self);
+}
+
+
+static void channel_section_init(ChannelSection* self)
+{
+    GtkBox* box = GTK_BOX(self);
+    GtkWidget* table = gtk_table_new( 3, 2, 1);
+    GtkWidget* lbl_chan = gtk_label_new("Channel");
+    GtkWidget* lbl_lower = gtk_label_new("Lower Vel.");
+    GtkWidget* lbl_upper = gtk_label_new("Upper Vel.");
+    
+    self->patch = -1;
+
+    gtk_table_attach_defaults(GTK_TABLE(table),lbl_chan,0,1,0,1);
+    gtk_widget_show(lbl_chan);
+    gtk_table_attach_defaults(GTK_TABLE(table),lbl_lower,0,1,1,2);
+    gtk_widget_show(lbl_lower);
+    gtk_table_attach_defaults(GTK_TABLE(table),lbl_upper,0,1,2,3);
+    gtk_widget_show(lbl_upper);
+
+    /* channel sliderbutton */
+    self->chan_sb = phin_slider_button_new_with_range(1, 1, MIDI_CHANS,1,0);
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(self->chan_sb),
+                                                        GUI_THRESHOLD);
+    gtk_table_attach_defaults(GTK_TABLE(table),self->chan_sb,1,2,0,1);
+    gtk_widget_show(self->chan_sb);
+
+    /* lower velocity slide */ 
+    self->lower_vel_sb = phin_slider_button_new_with_range(0, 0, 127, 1, 0);
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(self->lower_vel_sb),
+                                                        GUI_THRESHOLD);
+    gtk_table_attach_defaults(GTK_TABLE(table),self->lower_vel_sb,1,2,1,2);
+    gtk_widget_show(self->lower_vel_sb);
+
+    /* upper velocity slider */
+    self->upper_vel_sb = phin_slider_button_new_with_range(127, 0, 127, 1, 0);
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(self->upper_vel_sb),
+                                                        GUI_THRESHOLD);
+    gtk_table_attach_defaults(GTK_TABLE(table),self->upper_vel_sb,1,2,2,3);
+    gtk_widget_show(self->upper_vel_sb);
+    
+    gui_pack(box, table);
+    
+    /* done */
+    connect(self);
+}
+
+
+static void block(ChannelSection* self)
+{
+    g_signal_handlers_block_by_func(self->chan_sb, channel_cb, self);
+}
+
+
+static void unblock(ChannelSection* self)
+{
+    g_signal_handlers_unblock_by_func(self->chan_sb, channel_cb, self);
+}
+
+
+static void set_sensitive(ChannelSection* self, gboolean val)
+{
+    gtk_widget_set_sensitive(self->chan_sb, val);
+    gtk_widget_set_sensitive(self->lower_vel_sb, val);
+    gtk_widget_set_sensitive(self->upper_vel_sb, val);
+}
+
+
+GtkWidget* channel_section_new(void)
+{
+    return (GtkWidget*) g_object_new(CHANNEL_SECTION_TYPE, NULL);
+}
+
+
+void channel_section_set_patch(ChannelSection* self, int patch)
+{
+    int channel, lower_vel, upper_vel;
+
+    self->patch = patch;
+
+    if (patch < 0)
+        set_sensitive(self, FALSE);
+    else
+    {
+        set_sensitive(self, TRUE);
+        channel = patch_get_channel(patch);
+        lower_vel = patch_get_lower_vel(patch);
+        upper_vel = patch_get_upper_vel(patch);
+
+        block(self);
+        phin_slider_button_set_value(PHIN_SLIDER_BUTTON(self->chan_sb),
+                                                                channel+1);
+        phin_slider_button_set_value(PHIN_SLIDER_BUTTON(self->lower_vel_sb),
+                                                                lower_vel);
+        phin_slider_button_set_value(PHIN_SLIDER_BUTTON(self->upper_vel_sb),
+                                                                upper_vel);
+        unblock(self);
+    }
+}
+
+
+int channel_section_get_channel(ChannelSection* self)
+{
+    return (self->patch < 0) ? 0 : patch_get_channel(self->patch);
+}
diff --git a/src/gui/channelsection.h b/gui/channelsection.h
similarity index 97%
rename from src/gui/channelsection.h
rename to gui/channelsection.h
index c3f33bf..23de74a 100644
--- a/src/gui/channelsection.h
+++ b/gui/channelsection.h
@@ -58,6 +58,8 @@ struct _ChannelSection
     /* <private> */
     int patch;
     GtkWidget* chan_sb;
+    GtkWidget* lower_vel_sb;
+    GtkWidget* upper_vel_sb;
 };
 
 
diff --git a/src/gui/envelopetab.c b/gui/envelopetab.c
similarity index 80%
rename from src/gui/envelopetab.c
rename to gui/envelopetab.c
index 58e655c..0a5ae6b 100644
--- a/src/gui/envelopetab.c
+++ b/gui/envelopetab.c
@@ -23,7 +23,9 @@
 
 
 #include <gtk/gtk.h>
-#include <phat/phat.h>
+
+#include "phin.h"
+
 #include "envelopetab.h"
 #include "gui.h"
 #include "idselector.h"
@@ -79,7 +81,7 @@ static void envelope_tab_class_init(EnvelopeTabClass* klass)
 static void set_sensitive(EnvelopeTabPrivate* p, gboolean val)
 {
     /*  setting the table itself takes care of the labels,
-     *  but still need to set the phat widgets...
+     *  but still need to set the phin widgets...
      */
     gtk_widget_set_sensitive(p->env_table, val);
     gtk_widget_set_sensitive(p->delay_fan, val);
@@ -104,7 +106,7 @@ static void id_selector_cb(IDSelector* ids, EnvelopeTabPrivate* p)
 
 static void on_cb(GtkToggleButton* button, EnvelopeTabPrivate* p)
 {
-    patch_set_env_on(p->patch,
+    patch_set_env_active(p->patch,
             id_selector_get_id(ID_SELECTOR(p->idsel)),
                     gtk_toggle_button_get_active(button));
 }
@@ -116,65 +118,65 @@ static void on_cb2(GtkToggleButton* button, EnvelopeTabPrivate* p)
 }
 
 
-static void delay_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void delay_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_delay(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)),val);
 }
 
 
-static void attack_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void attack_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_attack(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
 
 
-static void hold_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void hold_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_hold(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
 
 
-static void decay_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void decay_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_decay(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
 
 
-static void sustain_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void sustain_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_sustain(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
 
 
-static void release_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void release_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_release(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
 
 
-static void key_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void key_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_key_amt(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
 
 /*
-static void vel_cb(PhatFanSlider* fan, EnvelopeTabPrivate* p)
+static void vel_cb(PhinFanSlider* fan, EnvelopeTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(fan);
+    float val = phin_fan_slider_get_value(fan);
     patch_set_env_vel_amt(p->patch,
         id_selector_get_id(ID_SELECTOR(p->idsel)), val);
 }
@@ -279,49 +281,49 @@ static void envelope_tab_init(EnvelopeTab* self)
 
     /* delay fan */
     gui_label_attach("Delay:", t, a1, a2, y, y + 1);
-    p->delay_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->delay_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->delay_fan, b1, b2, y, y + 1);
     ++y;
 
     /* attack fan */
     gui_label_attach("Attack:", t, a1, a2, y, y + 1);
-    p->attack_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->attack_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->attack_fan, b1, b2, y, y + 1);
     ++y;
 
     /* hold fan */
     gui_label_attach("Hold:", t, a1, a2, y, y + 1);
-    p->hold_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->hold_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->hold_fan, b1, b2, y, y + 1);
     ++y;
 
     /* decay fan */
     gui_label_attach("Decay:", t, a1, a2, y, y + 1);
-    p->decay_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->decay_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->decay_fan, b1, b2, y, y + 1);
     ++y;
 
     /* sustain fan */
     gui_label_attach("Sustain:", t, a1, a2, y, y + 1);
-    p->sustain_fan = phat_hfan_slider_new_with_range(0.7, 0.0, 1.0, 0.01);
+    p->sustain_fan = phin_hfan_slider_new_with_range(0.7, 0.0, 1.0, 0.01);
     gui_attach(t, p->sustain_fan, b1, b2, y, y + 1);
     ++y;
 
     /* release fan */
     gui_label_attach("Release:", t, a1, a2, y, y  + 1);
-    p->release_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->release_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->release_fan, b1, b2, y, y + 1);
     ++y;
 
     /* key fan */
     gui_label_attach("Key Track:", t, a1, a2, y, y + 1);
-    p->key_fan = phat_hfan_slider_new_with_range(0.1, -1.0, 1.0, 0.01);
+    p->key_fan = phin_hfan_slider_new_with_range(0.1, -1.0, 1.0, 0.01);
     gui_attach(t, p->key_fan, b1, b2, y, y + 1);
     ++y;
 
     /* vel fan
     gui_label_attach("Vel.Sens:", t, a1, a2, y, y + 1);
-    p->vel_fan = phat_hfan_slider_new_with_range(0.1, -1.0, 1.0, 0.01);
+    p->vel_fan = phin_hfan_slider_new_with_range(0.1, -1.0, 1.0, 0.01);
     gui_attach(t, p->vel_fan, b1, b2, y, y + 1);
     ++y;
     */
@@ -334,33 +336,33 @@ static void envelope_tab_init(EnvelopeTab* self)
 static void update_env(EnvelopeTabPrivate* p)
 {
     int i = p->patch;
-    float l, a, h, d, s, r, key, vel;
+    float l, a, h, d, s, r, key;
     bool on;
 
     int id;
 
     id = id_selector_get_id(ID_SELECTOR(p->idsel));
 
-    patch_get_env_delay(i, id, &l);
-    patch_get_env_attack(i, id, &a);
-    patch_get_env_hold(i, id, &h);
-    patch_get_env_decay(i, id, &d);
-    patch_get_env_sustain(i, id, &s);
-    patch_get_env_release(i, id, &r);
-    patch_get_env_on(i, id, &on);
-    patch_get_env_key_amt(i, id, &key);
+    l = patch_get_env_delay(i, id);
+    a = patch_get_env_attack(i, id);
+    h = patch_get_env_hold(i, id);
+    d = patch_get_env_decay(i, id);
+    s = patch_get_env_sustain(i, id);
+    r = patch_get_env_release(i, id);
+    on = patch_get_env_active(i, id);
+    key = patch_get_env_key_amt(i, id);
 /*  patch_get_env_vel_amt(i, id, &vel); */
 
     block(p);
 
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->delay_fan), l);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->attack_fan), a);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->hold_fan), h);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->decay_fan), d);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->sustain_fan), s);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->release_fan), r);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->key_fan), key);
-/*  phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->vel_fan), vel); */
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->delay_fan), l);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->attack_fan), a);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->hold_fan), h);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->decay_fan), d);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->sustain_fan), s);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->release_fan), r);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->key_fan), key);
+/*  phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->vel_fan), vel); */
 
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->env_check), on);
 
diff --git a/src/gui/envelopetab.h b/gui/envelopetab.h
similarity index 100%
rename from src/gui/envelopetab.h
rename to gui/envelopetab.h
diff --git a/gui/float_section.c b/gui/float_section.c
new file mode 100644
index 0000000..ff1487f
--- /dev/null
+++ b/gui/float_section.c
@@ -0,0 +1,246 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <gtk/gtk.h>
+
+#include "phin.h"
+
+#include "float_section.h"
+#include "gui.h"
+#include "patch_set_and_get.h"
+#include "names.h"
+
+#include "mod_src_gui.h"
+
+
+typedef struct _FloatSectionPrivate FloatSectionPrivate;
+
+#define FLOAT_SECTION_GET_PRIVATE(obj)  \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+        FLOAT_SECTION_TYPE, FloatSectionPrivate))
+
+struct _FloatSectionPrivate
+{
+    int             patch_id;
+    PatchFloatType  float_type;
+
+    GtkWidget*      float_fan;
+    GtkWidget*      mod_combo;
+    GtkWidget*      mod_amt;
+};
+
+
+G_DEFINE_TYPE(FloatSection, float_section, GTK_TYPE_VBOX);
+
+
+static void float_section_class_init(FloatSectionClass* klass)
+{
+    GObjectClass* object_class = G_OBJECT_CLASS(klass);
+    float_section_parent_class = g_type_class_peek_parent(klass);
+    g_type_class_add_private(object_class, sizeof(FloatSectionPrivate));
+}
+
+
+static void float_cb(GtkWidget* w, FloatSectionPrivate* p)
+{
+    float val = phin_fan_slider_get_value(PHIN_FAN_SLIDER(w));
+    patch_float_set_value(p->patch_id, p->float_type, val);
+}
+
+
+static void mod_combo_cb(GtkComboBox* combo, FloatSectionPrivate* p)
+{
+    int mod_src = mod_src_combo_get_mod_src_id(combo);
+    patch_float_set_mod_src(p->patch_id, p->float_type, mod_src);
+    gtk_widget_set_sensitive(p->mod_amt, (mod_src != MOD_SRC_NONE));
+}
+
+
+static void mod_amt_cb(GtkWidget* w, FloatSectionPrivate* p)
+{
+    patch_float_set_mod_amt(p->patch_id,
+                            p->float_type,
+                            phin_fan_slider_get_value(PHIN_FAN_SLIDER(w)));
+}
+
+
+static void block(FloatSectionPrivate* p)
+{
+    g_signal_handlers_block_by_func(p->float_fan,   float_cb,       p);
+    g_signal_handlers_block_by_func(p->mod_amt,     mod_amt_cb,     p);
+    g_signal_handlers_block_by_func(p->mod_combo,   mod_combo_cb,   p);
+}
+
+
+static void unblock(FloatSectionPrivate* p)
+{
+    g_signal_handlers_unblock_by_func(p->float_fan, float_cb,       p);
+    g_signal_handlers_unblock_by_func(p->mod_amt,   mod_amt_cb,     p);
+    g_signal_handlers_unblock_by_func(p->mod_combo, mod_combo_cb,   p);
+}
+
+
+
+static void float_section_init(FloatSection* self)
+{
+    FloatSectionPrivate* p = FLOAT_SECTION_GET_PRIVATE(self);
+    p->patch_id = -1;
+    p->float_type = PATCH_FLOAT_INVALID;
+    p->float_fan = 0;
+    p->mod_combo = 0;
+    p->mod_amt = 0;
+}
+
+
+void float_section_set_float( FloatSection* self, PatchFloatType float_type)
+{
+    FloatSectionPrivate* p = FLOAT_SECTION_GET_PRIVATE(self);
+    GtkBox* box;
+    GtkWidget* table;
+    GtkTable* t;
+
+    int y = 0;
+
+    int a1 = 0, a2 = 1;
+    int b1 = 1, b2 = 2;
+    int c1 = 2, c2 = 3;
+
+    float floatmin, floatmax, floatstep;
+
+    const char* float_names[] = { "Portamento Time" };
+
+    box = GTK_BOX(self);
+
+    p->float_type = float_type;
+
+    switch(float_type)
+    {
+    case PATCH_FLOAT_PORTAMENTO_TIME:
+        floatmin = 0.0;
+        floatmax = 1.0;
+        floatstep = 0.01;
+        break;
+
+    default:
+        debug("Bad News! unknown float type!\n");
+        return;
+    }
+
+    gtk_container_set_border_width(GTK_CONTAINER(self), GUI_BORDERSPACE);
+
+    table = gtk_table_new(3, 3, FALSE);
+    t = (GtkTable*)table;
+    gui_pack(box, table);
+
+    gui_attach(t, gui_title_new(float_names[float_type]), a1, c2, y, y + 1);
+    ++y;
+
+    /* title padding */
+    gui_attach(t, gui_vpad_new(GUI_TITLESPACE), a1, c2, y, y + 1);
+    ++y;
+
+    /* label column spacing (of some description!?) */
+    gui_attach(t, gui_hpad_new(GUI_TEXTSPACE), c1, c2, y, y + 1);
+    ++y;
+
+    p->float_fan = phin_hfan_slider_new_with_range(0.0, floatmin,
+                                                        floatmax,
+                                                        floatstep);
+    gui_attach(t, p->float_fan, b1, b2, y, y + 1);
+    ++y;
+
+    gui_label_attach("Mod:", t, a1, a2, y, y + 1);
+
+    p->mod_combo = mod_src_new_combo_with_cell();
+    mod_src_combo_set_model(GTK_COMBO_BOX(p->mod_combo), MOD_SRC_GLOBALS);
+    gui_attach(t, p->mod_combo, b1, b2, y, y + 1);
+
+    p->mod_amt = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    gui_attach(t, p->mod_amt, c1, c2, y, y + 1);
+    ++y;
+
+    g_signal_connect(G_OBJECT(p->float_fan),        "value-changed",
+                        G_CALLBACK(float_cb),       (gpointer)p);
+
+    g_signal_connect(G_OBJECT(p->mod_amt),          "value-changed",
+                        G_CALLBACK(mod_amt_cb),     (gpointer)p);
+
+    g_signal_connect(G_OBJECT(p->mod_combo),        "changed",
+                        G_CALLBACK(mod_combo_cb),   (gpointer)p);
+}
+
+
+void float_section_set_list_global(FloatSection* self)
+{
+    FloatSectionPrivate* p = FLOAT_SECTION_GET_PRIVATE(self);
+
+    mod_src_combo_set_model(GTK_COMBO_BOX(p->mod_combo), MOD_SRC_GLOBALS);
+}
+
+
+void float_section_set_list_all(FloatSection* self)
+{
+    FloatSectionPrivate* p = FLOAT_SECTION_GET_PRIVATE(self);
+
+    mod_src_combo_set_model(GTK_COMBO_BOX(p->mod_combo), MOD_SRC_ALL);
+}
+
+
+GtkWidget* float_section_new(void)
+{
+    return (GtkWidget*)g_object_new(FLOAT_SECTION_TYPE, NULL);
+}
+
+
+void float_section_set_patch(FloatSection* self, int patch_id)
+{
+    FloatSectionPrivate* p = FLOAT_SECTION_GET_PRIVATE(self);
+
+    float   assign;
+    int     modsrc;
+    float   mod_amt;
+
+    GtkTreeIter moditer;
+
+    p->patch_id = patch_id;
+
+    if (patch_id < 0)
+        return;
+
+    patch_float_get_all(patch_id, p->float_type,    &assign,
+                                                    &mod_amt,
+                                                    &modsrc);
+    block(p);
+
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->float_fan), assign);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->mod_amt), mod_amt);
+
+    if (!mod_src_combo_get_iter_with_id(GTK_COMBO_BOX(p->mod_combo),
+                                        modsrc,
+                                        &moditer))
+    {
+        debug("failed to get mod source id from combo box\n");
+    }
+
+    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(p->mod_combo), &moditer);
+
+    unblock(p);
+}
+
diff --git a/gui/float_section.h b/gui/float_section.h
new file mode 100644
index 0000000..5509ca0
--- /dev/null
+++ b/gui/float_section.h
@@ -0,0 +1,75 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or Boolify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef __FLOAT_SECTION__
+#define __FLOAT_SECTION__
+
+#include <gtk/gtk.h>
+
+
+#include "patch.h"
+
+
+G_BEGIN_DECLS
+
+#define FLOAT_SECTION_TYPE      (float_section_get_type())
+#define FLOAT_SECTION(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                                FLOAT_SECTION_TYPE, FloatSection))
+
+#define IS_FLOAT_SECTION(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                                FLOAT_SECTION_TYPE))
+
+#define FLOAT_SECTION_CLASS(klass)(G_TYPE_CHECK_CLASS_CAST ((klass),  \
+                                FLOAT_SECTION_TYPE, FloatSectionClass))
+
+#define IS_FLOAT_SECTION_CLASS(klass) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((klass), FLOAT_SECTION_TYPE))
+
+
+typedef struct _FloatSectionClass FloatSectionClass;
+typedef struct _FloatSection      FloatSection;
+
+
+struct _FloatSection
+{
+    GtkVBox parent_instance;
+};
+
+
+struct _FloatSectionClass
+{
+    GtkVBoxClass parent_class;
+     /* <private> */
+    void (*set_toggled)(FloatSection*);
+};
+
+
+GType       float_section_get_type(void);
+
+GtkWidget*  float_section_new(void);
+
+void        float_section_set_float(FloatSection*, PatchFloatType);
+void        float_section_set_patch(FloatSection*, int patch_id);
+
+
+G_END_DECLS
+
+
+#endif /* __FLOAT_SECTION__ */
diff --git a/gui/global_settings.c b/gui/global_settings.c
new file mode 100644
index 0000000..23c679b
--- /dev/null
+++ b/gui/global_settings.c
@@ -0,0 +1,287 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 Brendan S. Jones
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <libxml/parser.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+#include "phin.h"
+#include "global_settings.h"
+#include "petri-foo.h"
+#include "msg_log.h"
+
+
+#define SETTINGS_BASENAME "rc.xml"
+
+
+static global_settings* gbl_settings = 0;
+/*
+    init and read settings
+*/
+void settings_init()
+{
+    if (gbl_settings) settings_free();
+
+    gbl_settings  = malloc(sizeof(global_settings));
+    gbl_settings->last_sample_dir = strdup(getenv("HOME"));
+    gbl_settings->last_bank_dir = strdup(getenv("HOME"));
+
+    gbl_settings->filename = (char*) g_build_filename(
+                             g_get_user_config_dir(),
+                             g_get_prgname(),
+                             SETTINGS_BASENAME,
+                             NULL);
+
+    gbl_settings->log_lines =           DEFAULT_LOG_LINES;
+/*
+    gbl_settings->abs_max_sample_size = DEFAULT_ABS_MAX_SAMPLE;
+    gbl_settings->max_sample_size =     DEFAULT_MAX_SAMPLE;
+ */
+    settings_read((char*) gbl_settings->filename);
+}
+
+
+static gboolean xmlstr_to_gboolean(xmlChar* str)
+{
+    if (xmlStrcasecmp(str, BAD_CAST "true") == 0
+     || xmlStrcasecmp(str, BAD_CAST "on") == 0
+     || xmlStrcasecmp(str, BAD_CAST "yes") == 0)
+    {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+int settings_read(const char* path)
+{
+    xmlDocPtr   doc;
+    xmlNodePtr  noderoot;
+    xmlNodePtr  node1;
+    xmlNodePtr  node2;
+    xmlChar*    prop;
+
+    msg_log(MSG_MESSAGE, "Reading global settings from: %s\n",path);
+
+    doc = xmlParseFile (path);
+
+    if (doc == NULL)
+    {
+        msg_log(MSG_ERROR, "Failed to parse %s\n", path);
+        return -1;
+    }
+
+    noderoot = xmlDocGetRootElement(doc);
+
+    if (noderoot == NULL)
+    {
+        msg_log(MSG_WARNING, "%s is empty\n", path);
+        xmlFreeDoc(doc);
+        return -1;
+    }
+
+    if (xmlStrcmp(noderoot->name, BAD_CAST "Petri-Foo-Settings") != 0)
+    {
+        msg_log(MSG_ERROR,
+                "%s is not a valid 'Petri-Foo-Settings' file\n", path);
+        xmlFreeDoc(doc);
+        return -1;
+    }
+
+    for (node1 = noderoot->children;
+         node1 != NULL;
+         node1 = node1->next)
+    {
+        if (node1->type != XML_ELEMENT_NODE)
+            continue;
+
+        for ( node2 = node1->children;
+            node2 != NULL;
+            node2 = node2->next)
+        {
+            int n;
+
+            if (xmlStrcmp(node2->name, BAD_CAST "property") == 0)
+            {
+                prop = BAD_CAST xmlGetProp(node2, BAD_CAST "name");
+
+                if (xmlStrcmp(prop, BAD_CAST "last-sample-directory") == 0)
+                {
+                    free(gbl_settings->last_sample_dir);
+                    gbl_settings->last_sample_dir =
+                        (char*) xmlGetProp(node2, BAD_CAST "value");
+                }
+
+                if (xmlStrcmp(prop, BAD_CAST "last-bank-directory") == 0)
+                {
+                    free(gbl_settings->last_bank_dir);
+                    gbl_settings->last_bank_dir =
+                        (char*) xmlGetProp(node2, BAD_CAST "value");
+                }
+
+                if (xmlStrcmp(prop, BAD_CAST "sliders-use-fans") == 0)
+                {
+                    phin_fan_slider_set_fans_active(
+                        xmlstr_to_gboolean(xmlGetProp(node2,
+                                                    BAD_CAST "value")));
+                }
+
+                if (xmlStrcmp(prop, BAD_CAST "log-lines") == 0)
+                {
+                    xmlChar* vprop = xmlGetProp(node2, BAD_CAST "value");
+
+                    if (sscanf((const char*)vprop, "%d", &n) == 1)
+                        gbl_settings->log_lines = n;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+int settings_write()
+{
+    int rc;
+    char* config_dir;
+    char buf[CHARBUFSIZE];
+
+    xmlDocPtr   doc;
+    xmlNodePtr  noderoot;
+    xmlNodePtr  node1;
+    xmlNodePtr  node2;
+
+    msg_log(MSG_MESSAGE, "Writing global settings to: %s\n",
+                         gbl_settings->filename);
+
+    doc = xmlNewDoc(BAD_CAST "1.0");
+
+    if (!doc)
+    {
+        msg_log(MSG_ERROR, "XML error!\n");
+        return -1; 
+    }
+
+    config_dir = (char*) g_build_filename(g_get_user_config_dir(),
+                                          g_get_prgname(), NULL);
+
+    if (mkdir(config_dir, S_IRWXU) != 0)
+    {
+         if (errno != EEXIST)
+         {
+             msg_log(MSG_ERROR,
+                    "Could not create config directory: %s.\n",
+                    config_dir);
+             free(config_dir);
+             return -1;
+         }
+    }
+
+    free(gbl_settings->filename);
+    gbl_settings->filename = (char*) g_build_filename(config_dir, 
+                                                      SETTINGS_BASENAME,
+                                                      NULL);
+    free(config_dir);
+
+    noderoot = xmlNewDocNode(doc, NULL, BAD_CAST "Petri-Foo-Settings",NULL);
+
+    if (!noderoot)
+    {
+        msg_log(MSG_ERROR, "XML error!\n");
+        return -1;
+    }
+
+    xmlDocSetRootElement(doc, noderoot);
+
+    node1 = xmlNewTextChild(noderoot, NULL, BAD_CAST "global", NULL);
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "property", NULL);
+    xmlNewProp(node2, BAD_CAST "name", BAD_CAST "last-sample-directory");
+    xmlNewProp(node2, BAD_CAST "type", BAD_CAST "string");
+    xmlNewProp(node2, BAD_CAST "value",
+                      BAD_CAST gbl_settings->last_sample_dir);
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "property", NULL);
+    xmlNewProp(node2, BAD_CAST "name", BAD_CAST "last-bank-directory");
+    xmlNewProp(node2, BAD_CAST "type", BAD_CAST "string");
+    xmlNewProp(node2, BAD_CAST "value",
+                      BAD_CAST gbl_settings->last_bank_dir);
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "property", NULL);
+    xmlNewProp(node2, BAD_CAST "name", BAD_CAST "sliders-use-fans");
+    xmlNewProp(node2, BAD_CAST "type", BAD_CAST "boolean");
+    xmlNewProp(node2, BAD_CAST "value",
+                      BAD_CAST (phin_fan_slider_get_fans_active()
+                                    ? "true"
+                                    : "false"));
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "property", NULL);
+    xmlNewProp(node2, BAD_CAST "name", BAD_CAST "last-bank-directory");
+    xmlNewProp(node2, BAD_CAST "type", BAD_CAST "string");
+    xmlNewProp(node2, BAD_CAST "value",
+                      BAD_CAST gbl_settings->last_bank_dir);
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "property", NULL);
+    xmlNewProp(node2, BAD_CAST "name", BAD_CAST "log-lines");
+    xmlNewProp(node2, BAD_CAST "type", BAD_CAST "int");
+
+    snprintf(buf, CHARBUFSIZE, "%d", gbl_settings->log_lines);
+
+    xmlNewProp(node2, BAD_CAST "value", BAD_CAST buf);
+
+    debug("attempting to write file:%s\n",gbl_settings->filename);
+
+    rc = xmlSaveFormatFile(gbl_settings->filename, doc, 1);
+    xmlFreeDoc(doc);
+
+    return rc;
+}
+
+
+global_settings* settings_get(void)
+{
+    return gbl_settings;
+}
+
+
+void settings_free(void)
+{
+    if (gbl_settings == NULL)
+        return;
+
+    if (gbl_settings->filename) 
+        free(gbl_settings->filename);
+
+    if (gbl_settings->last_sample_dir) 
+        free(gbl_settings->last_sample_dir);
+
+    if (gbl_settings->last_bank_dir) 
+        free(gbl_settings->last_bank_dir);
+
+    free(gbl_settings);
+
+    return;
+}
diff --git a/src/dish_file.h b/gui/global_settings.h
similarity index 50%
copy from src/dish_file.h
copy to gui/global_settings.h
index 5d875fd..38d8896 100644
--- a/src/dish_file.h
+++ b/gui/global_settings.h
@@ -1,6 +1,6 @@
 /*  Petri-Foo is a fork of the Specimen audio sampler.
 
-    Copyright 2011 James W. Morris
+    Copyright 2011 Brendan Jones
 
     This file is part of Petri-Foo.
 
@@ -18,18 +18,37 @@
 */
 
 
-#ifndef DISH_FILE_H
-#define DISH_FILE_H
+#ifndef SETTINGS_H
+#define SETTINGS_H
 
 
-/*  recommended file extension for petri-foo data files:
-    (includes dot)
+/*  global settings
+    
  */
 
-const char* dish_file_extension(void);
+#define DEFAULT_LOG_LINES           100
+#define DEFAULT_ABS_MAX_SAMPLE      (1024 * 1024 * 1024)
+#define DEFAULT_MAX_SAMPLE          (1024 * 1024 * 25)
 
-int         dish_file_read(const char* name);
-int         dish_file_write(const char* name);
+
+typedef struct global_settings_def
+{
+    char*   filename;
+    char*   last_sample_dir;
+    char*   last_bank_dir;
+    int     log_lines;
+/*
+    int     abs_max_sample_size;
+    int     max_sample_size;
+ */
+} global_settings;
+
+
+void                settings_init(void);
+int                 settings_read(const char* path);
+int                 settings_write(void);
+global_settings*    settings_get(void);
+void                settings_free();
 
 
 #endif
diff --git a/src/gui/gui.c b/gui/gui.c
similarity index 75%
rename from src/gui/gui.c
rename to gui/gui.c
index 5716943..dd46667 100644
--- a/src/gui/gui.c
+++ b/gui/gui.c
@@ -30,23 +30,28 @@
 
 #include "instance.h"
 #include "petri-foo.h"
+#include "pf_error.h"
+#include "phin.h"
 
+#include "audio-settings.h"
+#include "bank-ops.h"
+#include "channelsection.h"
+#include "config.h"
 #include "driver.h"
-#include "mixer.h"
 #include "gui.h"
-#include "patchsection.h"
+#include "log_display.h"
 #include "mastersection.h"
-#include "channelsection.h"
 #include "midisection.h"
-#include "patchlist.h"
-#include "waveform.h"
-#include "sample-editor.h"
-#include "sample-selector.h"
-#include "bank-ops.h"
-#include "audio-settings.h"
+#include "mixer.h"
 #include "mod_src_gui.h"
+#include "msg_log.h"
+#include "patchlist.h"
+#include "patchsection.h"
 #include "patch_set_and_get.h"
 #include "patch_util.h"
+#include "sample-editor.h"
+#include "sample-selector.h"
+#include "waveform.h"
 
 
 /* windows */
@@ -59,10 +64,23 @@ static GtkWidget* patch_list;
 
 
 /* main menu */
+static GtkWidget* menubar = 0;
 static GtkWidget* menu_file = 0;
 static GtkWidget* menu_settings = 0;
 static GtkWidget* menu_patch = 0;
 static GtkWidget* menu_help = 0;
+static GtkWidget* menu_view = 0;
+static GtkWidget* menuitem_file_recent = 0;
+
+/* view menu */
+static GtkWidget* menu_view_log_display = 0;
+
+
+/* settings */
+static GtkWidget* menu_settings_fans = 0;
+
+/* current patch, makes passing patch id to sample editor easier */
+static int cur_patch = -1;
 
 
 GtkWidget* gui_title_new(const char* msg)
@@ -264,8 +282,8 @@ void cb_menu_patch_add(GtkWidget* menu_item, gpointer data)
 
         if (id < 0)
         {
-            errmsg("Failed to create a new patch (%s).\n",
-                                        patch_strerror(id));
+            msg_log(MSG_ERROR, "Failed to create a new patch (%s).\n",
+                                            pf_error_str(pf_error_get()));
             return;
         }
 
@@ -289,8 +307,8 @@ void cb_menu_patch_add_default(GtkWidget* menu_item, gpointer data)
 
     if (id < 0)
     {
-        errmsg("Failed to create a new patch (%s).\n",
-                                        patch_strerror(id));
+        msg_log(MSG_ERROR, "Failed to create a new patch (%s).\n",
+                                            pf_error_str(pf_error_get()));
         return;
     }
 
@@ -310,14 +328,14 @@ void cb_menu_patch_duplicate(GtkWidget* menu_item, gpointer data)
 
     if ((cp = patch_list_get_current_patch(PATCH_LIST(patch_list))) < 0)
     {   /* let's be a little more polite here shall we? */
-        debug ("Pardon, but exactly what am I to duplicate?\n");
+        msg_log(MSG_WARNING, "No patch to duplicate\n");
         return;
     }
 
     if ((val = patch_duplicate(cp)) < 0)
     {
-        errmsg ("Failed to create a new patch (%s).\n",
-        patch_strerror (val));
+        msg_log(MSG_ERROR, "Failed to duplicate patch (%s).\n",
+                                            pf_error_str(pf_error_get()));
         return;
     }
 
@@ -382,8 +400,8 @@ void cb_menu_patch_rename(GtkWidget* menu_item, gpointer data)
 
         if (val < 0)
         {
-            errmsg ("Failed to rename patch (%s).\n",
-            patch_strerror (val));
+            msg_log(MSG_ERROR, "Failed to rename patch (%s).\n",
+                                            pf_error_str(pf_error_get()));
             return;
         }
 
@@ -410,12 +428,7 @@ void cb_menu_patch_remove(GtkWidget* menu_item, gpointer data)
     }
 
     index = patch_list_get_current_index (PATCH_LIST(patch_list));
-    if ((val = patch_destroy (cp)) < 0)
-    {
-        errmsg ("Error removing patch %d (%s).\n", cp,
-        patch_strerror (val));
-        return;
-    }
+    patch_destroy(cp);
 
     if (index == 0)
         patch_list_update(PATCH_LIST(patch_list), index,
@@ -426,6 +439,25 @@ void cb_menu_patch_remove(GtkWidget* menu_item, gpointer data)
 }
 
 
+void cb_menu_view_log_display_showing(gboolean active)
+{
+    gtk_check_menu_item_set_active(
+        GTK_CHECK_MENU_ITEM(menu_view_log_display), active);
+}
+
+
+static void cb_menu_view_log_display(GtkWidget* widget, gpointer data)
+{
+    if (gtk_check_menu_item_get_active(
+        GTK_CHECK_MENU_ITEM(menu_view_log_display)))
+    {
+        log_display_show();
+    }
+    else
+        log_display_hide();
+}
+
+
 static void cb_menu_file_new_bank (GtkWidget * widget, gpointer data)
 {
     (void)widget;(void)data;
@@ -469,6 +501,15 @@ static void cb_menu_settings_audio (GtkWidget * widget, gpointer data)
 }
 
 
+static void cb_menu_settings_fans(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    phin_fan_slider_set_fans_active(
+        gtk_check_menu_item_get_active(
+            GTK_CHECK_MENU_ITEM(menu_settings_fans)));
+}
+
+
 static void cb_menu_help_stfu (GtkWidget* widget, gpointer data)
 {
     (void)widget;(void)data;
@@ -479,13 +520,13 @@ static void cb_menu_help_stfu (GtkWidget* widget, gpointer data)
 static void cb_menu_help_about (GtkWidget* widget, gpointer data)
 {
     (void)widget;
-    GdkPixbuf* logo;
-    const char* authors[] = { "Pete Bessman (original author)",
-                             "See the AUTHORS file for others", 0 };
-
-    /* should this be freed later on? */
-    logo = gdk_pixbuf_new_from_file(PIXMAPSDIR "petri-foo.png", NULL);
+    GdkPixbuf* logo = 0;
+    const char* authors[] = {   "Pete Bessman - original Specimen author",
+                                "James Morris - Petri-Foo creator",
+                                "See the AUTHORS file for others", 0 };
 
+/*  should this be freed later on?  */
+    logo = gdk_pixbuf_new_from_file(PIXMAPS_DIR "/petri-foo.png", NULL);
     gtk_show_about_dialog(
         GTK_WINDOW(data),
         "name", "Petri-Foo",
@@ -502,21 +543,48 @@ static void cb_menu_help_about (GtkWidget* widget, gpointer data)
 static void cb_patch_list_changed(PatchList* list, gpointer data)
 {
     (void)data;
-    int patch = patch_list_get_current_patch(list);
+    cur_patch = patch_list_get_current_patch(list);
 
-    debug("patch list changed!\n");
+    debug("patch list changed patch:%d!\n",cur_patch);
 
-    patch_section_set_patch(PATCH_SECTION(patch_section), patch);
-    midi_section_set_patch(MIDI_SECTION(midi_section), patch);
-    channel_section_set_patch(CHANNEL_SECTION(channel_section), patch);
+    if (cur_patch < 0)
+        sample_editor_hide();
+    else if (sample_editor_get_visible())
+        sample_editor_show(cur_patch);
+
+    patch_section_set_patch(PATCH_SECTION(patch_section), cur_patch);
+    midi_section_set_patch(MIDI_SECTION(midi_section), cur_patch);
+    channel_section_set_patch(CHANNEL_SECTION(channel_section), cur_patch);
 }
 
 
+static void cb_recent_chooser_item_activated (GtkRecentChooser *chooser
+               , gpointer *data)
+{
+    (void)data;
+    gchar *uri;
+    gchar *filename;
+    GtkRecentInfo *recentInfo;
+    debug("recent-menu item-activated");
+    uri = gtk_recent_chooser_get_current_uri (chooser);
+    filename = g_filename_from_uri(uri, NULL, NULL);
+    recentInfo = gtk_recent_chooser_get_current_item(chooser);
+    gtk_recent_manager_add_item(recent_manager, uri);
+    gtk_recent_info_unref(recentInfo);
+    if (bank_ops_open_recent(window, (char*) filename) == 0)
+    {
+        patch_list_update (PATCH_LIST(patch_list), 0, PATCH_LIST_INDEX);
+        master_section_update(MASTER_SECTION(master_section));
+    }
+     
+    g_free(uri);
+    g_free(filename);
+}
+
 int gui_init(void)
 {
     GtkWidget* window_vbox;
     GtkWidget* master_hbox;
-    GtkWidget* menubar;
     GtkWidget* vbox;
 
     debug ("Initializing GUI\n");
@@ -544,10 +612,14 @@ int gui_init(void)
             G_CALLBACK(cb_menu_file_new_bank),      window);
     gui_menu_add(menu_file, "Open Bank...",
             G_CALLBACK(cb_menu_file_open_bank),     window);
+
+    menuitem_file_recent = gtk_menu_item_new_with_label("Open Recent");
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu_file), menuitem_file_recent);   
     gui_menu_add(menu_file, "Save Bank",
             G_CALLBACK(cb_menu_file_save_bank),     window);
     gui_menu_add(menu_file, "Save Bank As...",
             G_CALLBACK(cb_menu_file_save_bank_as),  window);
+ 
     /* seperator */
     gui_menu_add(menu_file, NULL, NULL, NULL);
     gui_menu_add(menu_file, "Quit", G_CALLBACK(cb_quit), NULL);
@@ -565,18 +637,42 @@ int gui_init(void)
     gui_menu_add(menu_patch, "Remove",
             G_CALLBACK(cb_menu_patch_remove),       NULL);
 
+    /* view menu */
+    menu_view = gui_menu_add(menubar, "View", NULL, NULL);
+
+    menu_view_log_display =
+        gui_menu_check_add(menu_view, "Message Log", FALSE,
+            G_CALLBACK(cb_menu_view_log_display), window);
+
     /* settings menu */
     menu_settings = gui_menu_add(menubar, "Settings", NULL, NULL);
     gui_menu_add(menu_settings, "Audio...",
             G_CALLBACK(cb_menu_settings_audio),     NULL);
 
+    menu_settings_fans =
+        gtk_check_menu_item_new_with_label("Use slider fans");
+
+    gtk_check_menu_item_set_active(
+        GTK_CHECK_MENU_ITEM(menu_settings_fans), 
+            phin_fan_slider_get_fans_active());
+
+    g_signal_connect(GTK_OBJECT(menu_settings_fans), "toggled",
+        G_CALLBACK(cb_menu_settings_fans), NULL);
+
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu_settings),
+                                        menu_settings_fans);
+
+    gtk_widget_show(menu_settings_fans);
+
     /* help menu */
     menu_help = gui_menu_add(menubar, "Help", NULL, NULL);
-    gui_menu_add(menu_help, "Stuff You!",
+    gui_menu_add(menu_help, "All Sound Off!",
             G_CALLBACK(cb_menu_help_stfu),          NULL);
     gui_menu_add(menu_help, "About...",
             G_CALLBACK(cb_menu_help_about),         window);
 
+    gui_recent_files_load();
+
     gtk_widget_show_all(menubar);
 
     /* setup the main window's master hbox, and left and right boxes */
@@ -631,6 +727,7 @@ int gui_init(void)
     /* intialize children */
     sample_editor_init(window);
     audio_settings_init(window);
+    log_display_init(window);
 
     /* priming updates */
     master_section_update(MASTER_SECTION(master_section));
@@ -646,6 +743,7 @@ void gui_refresh(void)
     master_section_update(MASTER_SECTION(master_section));
     patch_list_update(PATCH_LIST(patch_list), 0, PATCH_LIST_INDEX);
     cb_patch_list_changed(PATCH_LIST(patch_list), NULL);
+    gui_recent_files_load();
 }
 
 
@@ -697,3 +795,51 @@ GtkWidget* gui_menu_add(GtkWidget* menu, const char* label, GCallback cb,
 
     return (submenu) ? submenu : item;
 }
+
+
+GtkWidget*
+gui_menu_check_add(GtkWidget* menu, const char* label,  gboolean active,
+                                                        GCallback cb,
+                                                        gpointer data)
+{
+    GtkWidget* item = 0;
+    GtkWidget* submenu = 0;
+
+    item = gtk_check_menu_item_new_with_label(label);
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
+    g_signal_connect(item, "toggled", cb, data);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+    return item;
+}
+
+/* recent items menu */
+void gui_recent_files_load(void) 
+{
+    GtkWidget* menuitem_to_append_to = NULL;
+    GtkRecentFilter *recent_filter;
+    GtkWidget *menuitem_file_recent_items;
+    recent_manager = gtk_recent_manager_get_default();
+    recent_filter = gtk_recent_filter_new();
+    gtk_recent_filter_add_mime_type(recent_filter, "application/x-petri-foo");
+    menuitem_file_recent_items = 
+            gtk_recent_chooser_menu_new_for_manager(recent_manager);
+    gtk_recent_chooser_add_filter(
+            GTK_RECENT_CHOOSER(menuitem_file_recent_items), recent_filter);
+    gtk_recent_chooser_set_show_tips(
+            GTK_RECENT_CHOOSER(menuitem_file_recent_items), TRUE);
+    gtk_recent_chooser_set_sort_type(
+            GTK_RECENT_CHOOSER(menuitem_file_recent_items),
+            GTK_RECENT_SORT_MRU);
+    gtk_recent_chooser_set_limit(
+            GTK_RECENT_CHOOSER(menuitem_file_recent_items), 10);
+    gtk_recent_chooser_set_local_only(
+            GTK_RECENT_CHOOSER(menuitem_file_recent_items), FALSE);
+    gtk_recent_chooser_menu_set_show_numbers(
+            GTK_RECENT_CHOOSER_MENU(menuitem_file_recent_items), TRUE);
+    g_signal_connect(GTK_OBJECT(menuitem_file_recent_items), "item-activated",
+                     G_CALLBACK(cb_recent_chooser_item_activated), window);
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem_file_recent), 
+            menuitem_file_recent_items);
+
+}
diff --git a/src/gui/gui.h b/gui/gui.h
similarity index 84%
rename from src/gui/gui.h
rename to gui/gui.h
index 594899c..7bd15e3 100644
--- a/src/gui/gui.h
+++ b/gui/gui.h
@@ -38,9 +38,12 @@ enum
     GUI_TEXTSPACE = 12,		/* space between a label and its control */
     GUI_BORDERSPACE = 12,	/* space between a border and its guts */
     GUI_THRESHOLD = 20,		/* threshold used for sliderbuttons */
-    GUI_REFRESH_TIMEOUT = 100	/* time in milliseconds between controller refreshes */
+    GUI_REFRESH_TIMEOUT = 100,	/* time in milliseconds between controller refreshes */
+    GUI_MAX_RECENT_FILES = 5 /* maximum recent files in Open Recent Bank menu */
 };
 
+/* Recent Manager */
+
 /* returns a titlefied label */
 GtkWidget*  gui_title_new(const char* msg);
 
@@ -74,6 +77,9 @@ int gui_init(void);
 /* refresh the gui's display */
 void gui_refresh(void);
 
+/* update the recently used files */
+void gui_recent_files_load(void);
+
 /* get the gui's PatchList widget */
 PatchList* gui_get_patch_list(void);
 
@@ -87,8 +93,17 @@ void cb_menu_patch_duplicate(   GtkWidget* menu_item, gpointer data);
 void cb_menu_patch_rename(      GtkWidget* menu_item, gpointer data);
 void cb_menu_patch_remove(      GtkWidget* menu_item, gpointer data);
 
+void cb_menu_view_log_display_showing(gboolean);
+
 
 GtkWidget* gui_menu_add(GtkWidget* menu, const char* label, GCallback cb,
                                                             gpointer data);
 
+GtkWidget*
+     gui_menu_check_add(GtkWidget* menu, const char* label, gboolean active,
+                                                            GCallback cb,
+                                                            gpointer data);
+
+GtkRecentManager *recent_manager;
+ 
 #endif /* __GUI_H__ */
diff --git a/src/gui/idselector.c b/gui/idselector.c
similarity index 100%
rename from src/gui/idselector.c
rename to gui/idselector.c
diff --git a/src/gui/idselector.h b/gui/idselector.h
similarity index 100%
rename from src/gui/idselector.h
rename to gui/idselector.h
diff --git a/src/gui/lfotab.c b/gui/lfotab.c
similarity index 85%
rename from src/gui/lfotab.c
rename to gui/lfotab.c
index cbf1ba3..50fb17d 100644
--- a/src/gui/lfotab.c
+++ b/gui/lfotab.c
@@ -23,7 +23,9 @@
 
 
 #include <gtk/gtk.h>
-#include <phat/phat.h>
+
+#include "phin.h"
+
 #include "lfotab.h"
 #include "gui.h"
 #include "idselector.h"
@@ -110,7 +112,7 @@ static void set_sensitive(LfoTabPrivate* p, gboolean val)
                                 GTK_TOGGLE_BUTTON(p->sync_radio));
 
     /*  setting the table itself takes care of the labels,
-     *  but still need to set the phat widgets...
+     *  but still need to set the phin widgets...
      */
     gtk_widget_set_sensitive(p->lfo_table,  val);
 
@@ -146,7 +148,7 @@ static void idsel_cb(IDSelector* ids, LfoTabPrivate* p)
 
 static void on_cb(GtkToggleButton* button, LfoTabPrivate* p)
 {
-    patch_set_lfo_on(p->patch_id, p->lfo_id,
+    patch_set_lfo_active(p->patch_id, p->lfo_id,
                     gtk_toggle_button_get_active(button));
 }
 
@@ -190,17 +192,17 @@ static void sync_cb2(GtkToggleButton* button, LfoTabPrivate* p)
 }
 
 
-static void freq_cb(PhatFanSlider* fan, LfoTabPrivate* p)
+static void freq_cb(PhinFanSlider* fan, LfoTabPrivate* p)
 {
     patch_set_lfo_freq( p->patch_id, p->lfo_id,
-                        phat_fan_slider_get_value(fan));
+                        phin_fan_slider_get_value(fan));
 }
 
 
-static void beats_cb(PhatSliderButton* button, LfoTabPrivate* p)
+static void beats_cb(PhinSliderButton* button, LfoTabPrivate* p)
 {
-    patch_set_lfo_beats(p->patch_id, p->lfo_id,
-                        phat_slider_button_get_value(button));
+    patch_set_lfo_sync_beats(p->patch_id, p->lfo_id,
+                        phin_slider_button_get_value(button));
 }
 
 static void positive_cb(GtkToggleButton* button, LfoTabPrivate* p)
@@ -210,17 +212,17 @@ static void positive_cb(GtkToggleButton* button, LfoTabPrivate* p)
 }
 
 
-static void delay_cb(PhatFanSlider* fan, LfoTabPrivate* p)
+static void delay_cb(PhinFanSlider* fan, LfoTabPrivate* p)
 {
     patch_set_lfo_delay(p->patch_id, p->lfo_id,
-                        phat_fan_slider_get_value(fan));
+                        phin_fan_slider_get_value(fan));
 }
 
 
-static void attack_cb(PhatFanSlider* fan, LfoTabPrivate* p)
+static void attack_cb(PhinFanSlider* fan, LfoTabPrivate* p)
 {
     patch_set_lfo_attack(p->patch_id, p->lfo_id,
-                        phat_fan_slider_get_value(fan));
+                        phin_fan_slider_get_value(fan));
 }
 
 static void mod_src_cb(GtkComboBox* combo, LfoTabPrivate* p)
@@ -246,7 +248,7 @@ static void mod_src_cb(GtkComboBox* combo, LfoTabPrivate* p)
 
 static void mod_amount_cb(GtkWidget* w, LfoTabPrivate* p)
 {
-    float val = phat_fan_slider_get_value(PHAT_FAN_SLIDER(w));
+    float val = phin_fan_slider_get_value(PHIN_FAN_SLIDER(w));
 
     if (w == p->fm1_amount)
         patch_set_lfo_fm1_amt(p->patch_id, p->lfo_id, val);
@@ -405,7 +407,7 @@ static void lfo_tab_init(LfoTab* self)
     /* freq */
     gui_label_attach("Hrtz:", t, a1, a2, y, y + 1);
     p->free_radio = gtk_radio_button_new(NULL);
-    p->freq_fan = phat_hfan_slider_new_with_range(5.0, 0.0, 50.0, 0.1);
+    p->freq_fan = phin_hfan_slider_new_with_range(5.0, 0.0, 50.0, 0.1);
     gui_attach(t, p->free_radio, b1, b2, y, y + 1);
     gui_attach(t, p->freq_fan, c1, c2, y, y + 1);
     ++y;
@@ -413,10 +415,10 @@ static void lfo_tab_init(LfoTab* self)
     /* sync */
     p->sync_radio = gtk_radio_button_new_from_widget(
                             GTK_RADIO_BUTTON(p->free_radio));
-    p->beats_sb = phat_slider_button_new_with_range(1.0, .25, 32.0, .25, 2);
-    phat_slider_button_set_format(PHAT_SLIDER_BUTTON(p->beats_sb),
+    p->beats_sb = phin_slider_button_new_with_range(1.0, .25, 32.0, .25, 2);
+    phin_slider_button_set_format(PHIN_SLIDER_BUTTON(p->beats_sb),
                                     -1, NULL, "Beats");
-    phat_slider_button_set_threshold(PHAT_SLIDER_BUTTON(p->beats_sb),
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(p->beats_sb),
                                     GUI_THRESHOLD);
     gui_attach(t, p->sync_radio, b1, b2, y, y + 1);
     gui_attach(t, p->beats_sb, c1, c2, y, y + 1);
@@ -430,7 +432,7 @@ static void lfo_tab_init(LfoTab* self)
     ++y;
 
     gui_label_attach("Amount:", t, a1, a2, y, y + 1);
-    p->fm1_amount = phat_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    p->fm1_amount = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
     gui_attach(t, p->fm1_amount, c1, c2, y, y + 1);
     ++y;
 
@@ -441,7 +443,7 @@ static void lfo_tab_init(LfoTab* self)
     ++y;
 
     gui_label_attach("Amount:", t, a1, a2, y, y + 1);
-    p->fm2_amount = phat_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    p->fm2_amount = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
     gui_attach(t, p->fm2_amount, c1, c2, y, y + 1);
     ++y;
 
@@ -469,13 +471,13 @@ static void lfo_tab_init(LfoTab* self)
 
     /* delay fan */
     p->delay_label = label = gui_label_attach("Delay:", t, a1, a2, y, y+1);
-    p->delay_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->delay_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->delay_fan, c1, c2, y, y + 1);
     ++y;
 
     /* attack fan */
     p->attack_label = label = gui_label_attach("Attack:", t, a1, a2, y,y+1);
-    p->attack_fan = phat_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
+    p->attack_fan = phin_hfan_slider_new_with_range(0.1, 0.0, 1.0, 0.01);
     gui_attach(t, p->attack_fan, c1, c2, y, y + 1);
     ++y;
 
@@ -486,7 +488,7 @@ static void lfo_tab_init(LfoTab* self)
     ++y;
 
     gui_label_attach("Amount:", t, a1, a2, y, y + 1);
-    p->am1_amount = phat_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    p->am1_amount = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
     gui_attach(t, p->am1_amount, c1, c2, y, y + 1);
     ++y;
 
@@ -497,7 +499,7 @@ static void lfo_tab_init(LfoTab* self)
     ++y;
 
     gui_label_attach("Amount:", t, a1, a2, y, y + 1);
-    p->am2_amount = phat_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    p->am2_amount = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
     gui_attach(t, p->am2_amount, c1, c2, y, y + 1);
     ++y;
 
@@ -512,8 +514,7 @@ static void update_lfo(LfoTabPrivate* p)
     LFOShape lfoshape;
     int shape;
     float freq, beats, delay, attack;
-    bool sync, positive;
-    bool on;
+    bool active, sync, positive;
     GtkTreeIter iter;
 
     int   fm1src, fm2src;
@@ -542,31 +543,32 @@ static void update_lfo(LfoTabPrivate* p)
     mod_src_combo_set_model(GTK_COMBO_BOX(p->am2_combo), mod_srcs);
     unblock(p);
 
-    debug("getting lfo (id:%d) data from patch\n", p->lfo_id);
+    debug("getting lfo (id:%d) data from patch:%d\n",
+                                p->lfo_id,p->patch_id);
 
-    patch_get_lfo_shape(    p->patch_id, p->lfo_id, &lfoshape);
-    patch_get_lfo_freq(     p->patch_id, p->lfo_id, &freq);
-    patch_get_lfo_beats(    p->patch_id, p->lfo_id, &beats);
+    lfoshape = patch_get_lfo_shape(     p->patch_id, p->lfo_id);
+    freq = patch_get_lfo_freq(          p->patch_id, p->lfo_id);
+    beats = patch_get_lfo_sync_beats(   p->patch_id, p->lfo_id);
 
     if (!mod_src_is_global(p->lfo_id))
     {
-        patch_get_lfo_delay(    p->patch_id, p->lfo_id, &delay);
-        patch_get_lfo_attack(   p->patch_id, p->lfo_id, &attack);
+        delay = patch_get_lfo_delay(    p->patch_id, p->lfo_id);
+        attack = patch_get_lfo_attack(  p->patch_id, p->lfo_id);
     }
 
-    patch_get_lfo_sync(     p->patch_id, p->lfo_id, &sync);
-    patch_get_lfo_positive( p->patch_id, p->lfo_id, &positive);
-    patch_get_lfo_on(       p->patch_id, p->lfo_id, &on);
+    sync = patch_get_lfo_sync(          p->patch_id, p->lfo_id);
+    positive = patch_get_lfo_positive(  p->patch_id, p->lfo_id);
+    active = patch_get_lfo_active(      p->patch_id, p->lfo_id);
 
-    patch_get_lfo_fm1_src(  p->patch_id, p->lfo_id, &fm1src);
-    patch_get_lfo_fm1_amt(  p->patch_id, p->lfo_id, &fm1amt);
-    patch_get_lfo_fm2_src(  p->patch_id, p->lfo_id, &fm2src);
-    patch_get_lfo_fm2_amt(  p->patch_id, p->lfo_id, &fm2amt);
+    fm1src = patch_get_lfo_fm1_src(  p->patch_id, p->lfo_id);
+    fm1amt = patch_get_lfo_fm1_amt(  p->patch_id, p->lfo_id);
+    fm2src = patch_get_lfo_fm2_src(  p->patch_id, p->lfo_id);
+    fm2amt = patch_get_lfo_fm2_amt(  p->patch_id, p->lfo_id);
 
-    patch_get_lfo_am1_src(  p->patch_id, p->lfo_id, &am1src);
-    patch_get_lfo_am1_amt(  p->patch_id, p->lfo_id, &am1amt);
-    patch_get_lfo_am2_src(  p->patch_id, p->lfo_id, &am2src);
-    patch_get_lfo_am2_amt(  p->patch_id, p->lfo_id, &am2amt);
+    am1src = patch_get_lfo_am1_src(  p->patch_id, p->lfo_id);
+    am1amt = patch_get_lfo_am1_amt(  p->patch_id, p->lfo_id);
+    am2src = patch_get_lfo_am2_src(  p->patch_id, p->lfo_id);
+    am2amt = patch_get_lfo_am2_amt(  p->patch_id, p->lfo_id);
 
     debug("getting mod src combo iter with id\n");
 
@@ -599,7 +601,7 @@ static void update_lfo(LfoTabPrivate* p)
 
     block(p);
 
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->freq_fan), freq);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->freq_fan), freq);
 
 
     if (mod_src_is_global(p->lfo_id))
@@ -611,15 +613,15 @@ static void update_lfo(LfoTabPrivate* p)
     }
     else
     {
-        phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->delay_fan), delay);
-        phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->attack_fan), attack);
+        phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->delay_fan), delay);
+        phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->attack_fan), attack);
         gtk_widget_show(p->delay_fan);
         gtk_widget_show(p->delay_label);
         gtk_widget_show(p->attack_fan);
         gtk_widget_show(p->attack_label);
     }
 
-    phat_slider_button_set_value(PHAT_SLIDER_BUTTON(p->beats_sb), beats);
+    phin_slider_button_set_value(PHIN_SLIDER_BUTTON(p->beats_sb), beats);
 
     if (sync)
         gtk_toggle_button_set_active(
@@ -650,12 +652,12 @@ static void update_lfo(LfoTabPrivate* p)
     gtk_combo_box_set_active_iter(GTK_COMBO_BOX(p->am1_combo), &am1iter);
     gtk_combo_box_set_active_iter(GTK_COMBO_BOX(p->am2_combo), &am2iter);
 
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->fm1_amount), fm1amt);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->fm2_amount), fm2amt);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->am1_amount), am1amt);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->am2_amount), am2amt);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->fm1_amount), fm1amt);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->fm2_amount), fm2amt);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->am1_amount), am1amt);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->am2_amount), am2amt);
 
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->lfo_check), on);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->lfo_check), active);
 
     unblock(p);
 }
diff --git a/src/gui/lfotab.h b/gui/lfotab.h
similarity index 100%
rename from src/gui/lfotab.h
rename to gui/lfotab.h
diff --git a/gui/log_display.c b/gui/log_display.c
new file mode 100644
index 0000000..6b41061
--- /dev/null
+++ b/gui/log_display.c
@@ -0,0 +1,154 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a Specimen original, modified 2011
+*/
+
+
+#include <gtk/gtk.h>
+
+#include "log_display.h"
+#include "gui.h"
+#include "msg_log.h"
+#include "petri-foo.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+
+static GtkWidget*   window;
+static GtkWidget*   textview;
+
+
+
+static void cb_close (GtkWidget* widget, gpointer data)
+{
+    (void)widget; (void)data;
+    printf("\n\nclose\n\n");
+    gtk_widget_hide (window);
+    cb_menu_view_log_display_showing(false);
+}
+
+
+static void msg_log_callback(const char* msg, int msg_base_type)
+{
+    GtkTextBuffer*  buffer;
+    GtkTextIter     iter;
+    GtkTextMark*    mark;
+    const char* tag = 0;
+
+if (GTK_IS_TEXT_VIEW(textview))
+{
+printf("%p ***IS*** textview\n", textview);
+}
+else
+{
+printf("%p ***IS NOT *** textview\n", textview);
+}
+
+    switch(msg_base_type)
+    {
+    case MSG_TYPE_DEBUG:    tag = "debug";      break;
+    case MSG_TYPE_WARNING:  tag = "warning";    break;
+    case MSG_TYPE_ERROR:    tag = "error";      break;
+    case MSG_TYPE_CRITICAL: tag = "critical";   break;
+    default:                tag = "message";    break;
+    }
+
+    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
+    gtk_text_buffer_get_end_iter(buffer, &iter);
+
+    gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, msg, -1,
+                                                        tag, NULL);
+
+    mark = gtk_text_buffer_get_mark (buffer, "scroll");
+    gtk_text_buffer_move_mark (buffer, mark, &iter);
+    gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(textview), mark);
+}
+
+
+void log_display_show(void)
+{
+     gtk_widget_show(window);
+}
+
+
+void log_display_hide(void)
+{
+     gtk_widget_hide(window);
+}
+
+
+void log_display_init(GtkWidget* parent)
+{
+    GtkWidget* swindow;
+    GtkWidget* vbox;
+
+    GtkTextBuffer*  buffer;
+    GtkTextIter iter;
+
+    debug("Initializing audio settings window\n");
+
+    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW (window), "Petri-Foo Message Log");
+    gtk_window_set_default_size(GTK_WINDOW(window), 700, 160);
+    gtk_window_set_modal (GTK_WINDOW (window), FALSE);
+
+    g_signal_connect(window, "delete-event",
+                                G_CALLBACK(cb_close), NULL);
+
+    gtk_container_set_border_width(GTK_CONTAINER(window), GUI_SPACING);
+
+    vbox = gtk_vbox_new(FALSE, GUI_SPACING);
+    gtk_container_add(GTK_CONTAINER(window), vbox);
+    gtk_widget_show(vbox);
+
+    swindow = gtk_scrolled_window_new(NULL, NULL);
+    gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0);
+    gtk_widget_show(swindow);
+
+    textview = gtk_text_view_new();
+    gtk_container_add(GTK_CONTAINER(swindow), textview);
+    gtk_widget_show(textview);
+
+    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
+
+    /*  didn't need this scroll mark until colour tags were added. */
+    gtk_text_buffer_get_end_iter(buffer, &iter);
+    gtk_text_buffer_create_mark(buffer, "scroll", &iter, TRUE);
+
+    gtk_text_buffer_create_tag(buffer,  "debug",
+                                        "foreground", "blue", NULL);
+
+    gtk_text_buffer_create_tag(buffer,  "message", NULL);
+
+    gtk_text_buffer_create_tag(buffer,  "warning",
+                                        "foreground", "purple", NULL);
+
+    gtk_text_buffer_create_tag(buffer,  "error",
+                                        "foreground", "red", NULL);
+
+    gtk_text_buffer_create_tag(buffer,  "critical",
+                                        "foreground", "red", NULL);
+
+
+    /* Tell msg log to use our callback which updates the text view */
+    msg_log_set_message_cb(msg_log_callback);
+}
diff --git a/src/dish_file.h b/gui/log_display.h
similarity index 74%
copy from src/dish_file.h
copy to gui/log_display.h
index 5d875fd..1bb519d 100644
--- a/src/dish_file.h
+++ b/gui/log_display.h
@@ -18,18 +18,16 @@
 */
 
 
-#ifndef DISH_FILE_H
-#define DISH_FILE_H
+#ifndef LOG_DISPLAY_H
+#define LOG_DISPLAY_H
 
 
-/*  recommended file extension for petri-foo data files:
-    (includes dot)
- */
+#include <gtk/gtk.h>
 
-const char* dish_file_extension(void);
 
-int         dish_file_read(const char* name);
-int         dish_file_write(const char* name);
+void log_display_init(GtkWidget* parent);
+void log_display_show(void);
+void log_display_hide(void);
 
 
 #endif
diff --git a/src/gui/mastersection.c b/gui/mastersection.c
similarity index 91%
rename from src/gui/mastersection.c
rename to gui/mastersection.c
index d406603..9ebef13 100644
--- a/src/gui/mastersection.c
+++ b/gui/mastersection.c
@@ -23,7 +23,9 @@
 
 
 #include <gtk/gtk.h>
-#include <phat/phat.h>
+
+#include "phin.h"
+
 #include "mastersection.h"
 #include "petri-foo.h"
 #include "gui.h"
@@ -41,11 +43,11 @@ static void master_section_class_init(MasterSectionClass* klass)
 }
 
 
-static void amplitude_changed_cb(PhatFanSlider* slider, gpointer data)
+static void amplitude_changed_cb(PhinFanSlider* slider, gpointer data)
 {
     (void)data;
     float val;
-    val = phat_fan_slider_get_value(slider);
+    val = phin_fan_slider_get_value(slider);
     mixer_set_amplitude(val);
 }
 
@@ -62,7 +64,7 @@ static void master_section_init(MasterSection* self)
     /* amplitude */
     label = gtk_label_new(NULL);
     self->amplitude_fan =
-        phat_hfan_slider_new_with_range(DEFAULT_AMPLITUDE, 0.0, 1.0, 0.1);
+        phin_hfan_slider_new_with_range(DEFAULT_AMPLITUDE, 0.0, 1.0, 0.1);
     hbox = gtk_hbox_new(FALSE, GUI_TEXTSPACE);
 
     gtk_label_set_markup(GTK_LABEL(label), "<b>Master</b>");
@@ -100,7 +102,7 @@ void master_section_update(MasterSection* self)
     g_signal_handlers_block_by_func(self->amplitude_fan, 
                                     (gpointer)amplitude_changed_cb, NULL);
 
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(self->amplitude_fan), amplitude);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(self->amplitude_fan), amplitude);
     g_signal_handlers_unblock_by_func(self->amplitude_fan, amplitude_changed_cb, NULL);
 }
     
diff --git a/src/gui/mastersection.h b/gui/mastersection.h
similarity index 100%
rename from src/gui/mastersection.h
rename to gui/mastersection.h
diff --git a/src/gui/midisection.c b/gui/midisection.c
similarity index 89%
rename from src/gui/midisection.c
rename to gui/midisection.c
index 462ea70..ee435a6 100644
--- a/src/gui/midisection.c
+++ b/gui/midisection.c
@@ -23,8 +23,10 @@
 
 
 #include <gtk/gtk.h>
-#include <phat/phat.h>
-#include <libgnomecanvas/libgnomecanvas.h>
+/*#include <libgnomecanvas/libgnomecanvas.h>*/
+
+#include "phin.h"
+
 #include "midisection.h"
 #include "petri-foo.h"
 #include "gui.h"
@@ -120,8 +122,8 @@ static gboolean range_cb(GnomeCanvasItem* item, GdkEvent* event,
 
     list = gui_get_patch_list();
 
-    clicked = event->button.x / PHAT_KEYBOARD_KEY_WIDTH;
-    note = patch_get_note(p->patch);
+    clicked = event->button.x / PHIN_KEYBOARD_KEY_WIDTH;
+    note =  patch_get_root_note(p->patch);
     lower = patch_get_lower_note(p->patch);
     upper = patch_get_upper_note(p->patch);
 
@@ -156,20 +158,20 @@ static gboolean range_cb(GnomeCanvasItem* item, GdkEvent* event,
 
     /* reposition note */
     gnome_canvas_item_set(p->note, "x1",
-                    (gdouble)(note * PHAT_KEYBOARD_KEY_WIDTH - 1),  NULL);
+                    (gdouble)(note * PHIN_KEYBOARD_KEY_WIDTH - 1),  NULL);
     gnome_canvas_item_set(p->note, "x2",
-                    (gdouble) (note * PHAT_KEYBOARD_KEY_WIDTH
-                                    + PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                    (gdouble) (note * PHIN_KEYBOARD_KEY_WIDTH
+                                    + PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
 
     /* reposition range */
     gnome_canvas_item_set(p->range, "x1",
-                    (gdouble)(lower * PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                    (gdouble)(lower * PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
     gnome_canvas_item_set(p->range, "x2",
-                    (gdouble)(upper * PHAT_KEYBOARD_KEY_WIDTH
-                                    + PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                    (gdouble)(upper * PHIN_KEYBOARD_KEY_WIDTH
+                                    + PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
 
     /* apply changes */
-    patch_set_note(p->patch, note);
+    patch_set_root_note(p->patch, note);
     patch_set_lower_note(p->patch, lower);
     patch_set_upper_note(p->patch, upper);
 
@@ -234,7 +236,7 @@ static void midi_section_init(MidiSection* self)
     p->ignore = FALSE;
     x1 = 0;
     y1 = 0;
-    x2 = (PHAT_KEYBOARD_KEY_WIDTH * MIDI_NOTES);
+    x2 = (PHIN_KEYBOARD_KEY_WIDTH * MIDI_NOTES);
     y2 = HEIGHT;
 
     /* adjustment */
@@ -270,7 +272,7 @@ static void midi_section_init(MidiSection* self)
 					gnome_canvas_rect_get_type(),
 					"x1", (gdouble)x1,
 					"y1", (gdouble)y1,
-					"x2", (gdouble)PHAT_KEYBOARD_KEY_WIDTH,
+					"x2", (gdouble)PHIN_KEYBOARD_KEY_WIDTH,
 					"y2", (gdouble)y2,
 					"fill-color-rgba", RANGE_COLOR,
 					"outline-color", "black",
@@ -282,7 +284,7 @@ static void midi_section_init(MidiSection* self)
 				       gnome_canvas_rect_get_type(),
 				       "x1", (gdouble)x1,
 				       "y1", (gdouble)y1,
-				       "x2", (gdouble)PHAT_KEYBOARD_KEY_WIDTH,
+				       "x2", (gdouble)PHIN_KEYBOARD_KEY_WIDTH,
 				       "y2", (gdouble)y2,
 				       "fill-color-rgba", NOTE_COLOR,
 				       "outline-color", "black",
@@ -313,7 +315,7 @@ static void midi_section_init(MidiSection* self)
 
 
     /* keyboard */
-    p->keyboard = phat_hkeyboard_new(p->adj, MIDI_NOTES, TRUE);
+    p->keyboard = phin_hkeyboard_new(p->adj, MIDI_NOTES, TRUE);
     gtk_box_pack_start(box, p->keyboard, FALSE, FALSE, 0);
     gtk_widget_show(p->keyboard);
 
@@ -365,24 +367,24 @@ void midi_section_set_patch(MidiSection* self, int patch)
 
         set_sensitive(p, TRUE);
 
-        note = patch_get_note(patch);
+        note =  patch_get_root_note(patch);
         lower = patch_get_lower_note(patch);
         upper = patch_get_upper_note(patch);
 
         block(p);
 
         gnome_canvas_item_set(p->note, "x1",
-                (gdouble)(note * PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                (gdouble)(note * PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
         gnome_canvas_item_set(p->note, "x2",
-                (gdouble)(note * PHAT_KEYBOARD_KEY_WIDTH
-                               + PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                (gdouble)(note * PHIN_KEYBOARD_KEY_WIDTH
+                               + PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
         gnome_canvas_item_show(p->note);
 
         gnome_canvas_item_set(p->range, "x1",
-                (gdouble) (lower * PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                (gdouble) (lower * PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
         gnome_canvas_item_set(p->range, "x2",
-                (gdouble) (upper * PHAT_KEYBOARD_KEY_WIDTH
-                                 + PHAT_KEYBOARD_KEY_WIDTH - 1), NULL);
+                (gdouble) (upper * PHIN_KEYBOARD_KEY_WIDTH
+                                 + PHIN_KEYBOARD_KEY_WIDTH - 1), NULL);
         if (lower != upper)
             gnome_canvas_item_show(p->range);
         else
diff --git a/src/gui/midisection.h b/gui/midisection.h
similarity index 100%
rename from src/gui/midisection.h
rename to gui/midisection.h
diff --git a/src/gui/mod_section.c b/gui/mod_section.c
similarity index 78%
rename from src/gui/mod_section.c
rename to gui/mod_section.c
index fe04eab..f82ad69 100644
--- a/src/gui/mod_section.c
+++ b/gui/mod_section.c
@@ -17,9 +17,10 @@
     along with Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-
+#include <assert.h>
 #include <gtk/gtk.h>
-#include <phat/phat.h>
+
+#include "phin.h"
 
 #include "mod_section.h"
 #include "gui.h"
@@ -64,7 +65,7 @@ static void mod_section_class_init(ModSectionClass* klass)
 
 static void param1_cb(GtkWidget* w, ModSectionPrivate* p)
 {
-    float val = phat_fan_slider_get_value(PHAT_FAN_SLIDER(w));
+    float val = phin_fan_slider_get_value(PHIN_FAN_SLIDER(w));
     patch_param_set_value(p->patch_id, p->param, val);
 }
 
@@ -72,27 +73,28 @@ static void param2_cb(GtkWidget* w, ModSectionPrivate* p)
 {
     if (p->param == PATCH_PARAM_PITCH)
     {
-        float val = phat_slider_button_get_value(PHAT_SLIDER_BUTTON(w));
+        float val = phin_slider_button_get_value(PHIN_SLIDER_BUTTON(w));
         patch_set_pitch_steps(p->patch_id, val);
     }
 }
 
 static void vel_sens_cb(GtkWidget* w, ModSectionPrivate* p)
 {
-    float val  = phat_fan_slider_get_value(PHAT_FAN_SLIDER(w));
-    patch_set_vel_amount(p->patch_id, p->param, val);
+    float val  = phin_fan_slider_get_value(PHIN_FAN_SLIDER(w));
+    patch_param_set_vel_amount(p->patch_id, p->param, val);
 }
 
 static void key_track_cb(GtkWidget* w, ModSectionPrivate* p)
 {
-    float val  = phat_fan_slider_get_value(PHAT_FAN_SLIDER(w));
-    patch_set_key_amount(p->patch_id, p->param, val);
+    float val  = phin_fan_slider_get_value(PHIN_FAN_SLIDER(w));
+    patch_param_set_key_amount(p->patch_id, p->param, val);
 }
 
 
 static void mod_src_cb(GtkComboBox* combo, ModSectionPrivate* p)
 {
     int i;
+    gboolean active;
 
     for (i = 0; i < MAX_MOD_SLOTS; ++i)
         if (combo == GTK_COMBO_BOX(p->mod_combo[i]))
@@ -104,7 +106,8 @@ static void mod_src_cb(GtkComboBox* combo, ModSectionPrivate* p)
         return;
     }
 
-    mod_src_callback_helper(p->patch_id, i, combo, p->param );
+    active = mod_src_callback_helper(p->patch_id, i, combo, p->param);
+    gtk_widget_set_sensitive(p->mod_amount[i], active);
 }
 
 static void mod_amount_cb(GtkWidget* w, ModSectionPrivate* p)
@@ -115,16 +118,12 @@ static void mod_amount_cb(GtkWidget* w, ModSectionPrivate* p)
 
     if (p->param == PATCH_PARAM_AMPLITUDE)
         --last_slot;
-    else
-    {
-        if (p->param == PATCH_PARAM_PITCH)
-            val = phat_slider_button_get_value(PHAT_SLIDER_BUTTON(w))
-                                                / PATCH_MAX_PITCH_STEPS;
-        else
-            val = phat_fan_slider_get_value(PHAT_FAN_SLIDER(w));
-    }
 
-    debug("amount val:%f\n",val);
+    if (p->param == PATCH_PARAM_PITCH)
+        val = phin_slider_button_get_value(PHIN_SLIDER_BUTTON(w))
+                                                / PATCH_MAX_PITCH_STEPS;
+    else
+        val = phin_fan_slider_get_value(PHIN_FAN_SLIDER(w));
 
     for (i = 0; i < last_slot; ++i)
         if (w == p->mod_amount[i])
@@ -136,7 +135,7 @@ static void mod_amount_cb(GtkWidget* w, ModSectionPrivate* p)
         return;
     }
 
-    patch_set_mod_amt(p->patch_id, p->param, i, val);
+    patch_param_set_mod_amt(p->patch_id, p->param, i, val);
 }
 
 
@@ -246,25 +245,6 @@ unblock_mod_srcs:
 }
 
 
-
-/*  reflect certain midi cc changes of parameters in the gui
-    no longer required
-
-static gboolean refresh(gpointer data)
-{
-    ModSection* self = MOD_SECTION(data);
-    ModSectionPrivate* p = MOD_SECTION_GET_PRIVATE(self);
-
-    if (p->patch_id < 0)
-        return TRUE;
-
-    mod_section_set_patch(self, p->patch_id);
-
-    return TRUE;
-}
- */
-
-
 static void mod_section_init(ModSection* self)
 {
     ModSectionPrivate* p = MOD_SECTION_GET_PRIVATE(self);
@@ -291,10 +271,13 @@ void mod_section_set_param(ModSection* self, PatchParamType param)
 
     float range_low = 0.0;
     float range_hi = 1.0;
+    gboolean logscale = FALSE;
 
     const char* lstr;
     const char** param_names = names_params_get();
 
+    char buf[80];
+
     int i;
     int y = 0;
     int env_slot = -1;
@@ -304,6 +287,8 @@ void mod_section_set_param(ModSection* self, PatchParamType param)
     int b1 = 1, b2 = 2;
     int c1 = 2, c2 = 3;
 
+debug("creating mod section...\n");
+
     box = GTK_BOX(self);
     p->param = param;
 
@@ -318,12 +303,8 @@ void mod_section_set_param(ModSection* self, PatchParamType param)
     gui_attach(t, gui_title_new(param_names[param]), a1, c2, y, y + 1);
     ++y;
 
-    /* indentation */
-    gui_attach(t, gui_hpad_new(GUI_INDENT), a1, a2, y, y + 1);
-    ++y;
-
     /* title padding */
-    gui_attach(t, gui_vpad_new(GUI_TITLESPACE), b1, b2, y, y + 1);
+    gui_attach(t, gui_vpad_new(GUI_TITLESPACE), a1, c2, y, y + 1);
     ++y;
 
     /* label column spacing (of some description!?) */
@@ -337,24 +318,38 @@ void mod_section_set_param(ModSection* self, PatchParamType param)
     {
     case PATCH_PARAM_AMPLITUDE:
         lstr = "Level:";
-        env_slot = --last_mod_slot;
+        env_slot = EG_MOD_SLOT;
+        --last_mod_slot;
         break;
 
-    case PATCH_PARAM_PANNING:   lstr = "Position:"; break;
-    case PATCH_PARAM_PITCH:     lstr = "Tuning:";   break;
-                                                    break;
-    default:
-        lstr = param_names[param];
+    case PATCH_PARAM_PANNING:
+        lstr = "Position:";
+        range_low = -1.0;
+        break;
+
+    case PATCH_PARAM_PITCH:
+        lstr = "Tuning:";
+        range_low = -1.0;
         break;
+    case PATCH_PARAM_RESONANCE:
+        lstr = "Q:";
+        /* broken impl in Phat/Phin: logscale = TRUE; */
+        break;
+    case PATCH_PARAM_CUTOFF:
+        snprintf(buf, 80, "%s:", param_names[param]);
+        lstr = buf;
+        break;
+    default:
+        assert(0);
+        return;
     }
 
     gui_label_attach(lstr, t, a1, a2, y, y + 1);
 
-    if (param == PATCH_PARAM_PANNING || param == PATCH_PARAM_PITCH)
-        range_low = -1.0;
-
-    p->param1 = phat_hfan_slider_new_with_range(0.0, range_low,
+    p->param1 = phin_hfan_slider_new_with_range(0.0, range_low,
                                                         range_hi,   0.1);
+    phin_fan_slider_set_log(PHIN_FAN_SLIDER(p->param1), logscale);
+
     gui_attach(t, p->param1, b1, b2, y, y + 1);
 
     if (param == PATCH_PARAM_PITCH)
@@ -366,13 +361,13 @@ void mod_section_set_param(ModSection* self, PatchParamType param)
 
     /* velocity sensitivity */
     gui_label_attach("Vel.Sens:", t, a1, a2, y, y + 1);
-    p->vel_sens = phat_hfan_slider_new_with_range(0.0, 0.0, 1.0, 0.1);
+    p->vel_sens = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
     gui_attach(t, p->vel_sens, b1, b2, y, y + 1);
     ++y;
 
     /* key tracking */
     gui_label_attach("Key Track:", t, a1, a2, y, y + 1);
-    p->key_track = phat_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
+    p->key_track = phin_hfan_slider_new_with_range(0.0, -1.0, 1.0, 0.1);
     gui_attach(t, p->key_track, b1, b2, y, y + 1);
     ++y;
 
@@ -401,23 +396,13 @@ create_mod_srcs:
         if (param == PATCH_PARAM_PITCH)
             p->mod_amount[i] = mod_src_new_pitch_adjustment();
         else
-            p->mod_amount[i] = phat_hfan_slider_new_with_range(0.0, -1.0,
+            p->mod_amount[i] = phin_hfan_slider_new_with_range(0.0, -1.0,
                                                             1.0, 0.1);
         gui_attach(t, p->mod_amount[i], c1, c2, y, y + 1);
         ++y;
     }
 
-    /* done */
     connect(p);
-
-    /*  add a timeout to allow the gui to reflect changes in
-        parameters which are changeable via midi cc mesages
-
-        nb: no longer required...
-
-    p->refresh = g_timeout_add(GUI_REFRESH_TIMEOUT, refresh,
-                                                    (gpointer)self);
-     */
 }
 
 
@@ -453,10 +438,10 @@ GtkWidget* mod_section_new(void)
 void mod_section_set_patch(ModSection* self, int patch_id)
 {
     ModSectionPrivate* p = MOD_SECTION_GET_PRIVATE(self);
-    float param1 = PATCH_PARAM_INVALID;
-    float param2 = PATCH_PARAM_INVALID;
-    float vsens;
-    float ktrack;
+    float param1;
+    float param2 = 0;
+    float vel_amt;
+    float key_trk;
 
     int     i;
     int     last_slot = MAX_MOD_SLOTS;
@@ -473,24 +458,24 @@ void mod_section_set_patch(ModSection* self, int patch_id)
     if (p->mod_only)
         goto get_mod_srcs;
 
-    patch_param_get_value(p->patch_id, p->param, &param1);
+    param1 = patch_param_get_value(p->patch_id, p->param);
 
     if (p->param == PATCH_PARAM_PITCH)
         param2 = patch_get_pitch_steps(p->patch_id);
     else if (p->param == PATCH_PARAM_AMPLITUDE)
         --last_slot;
 
-    patch_get_vel_amount(patch_id, p->param, &vsens);
-    patch_get_key_amount(patch_id, p->param, &ktrack);
+    vel_amt = patch_param_get_vel_amount(patch_id, p->param);
+    key_trk = patch_param_get_key_amount(patch_id, p->param);
 
 get_mod_srcs:
 
     for (i = 0; i < MAX_MOD_SLOTS; ++i)
     {
-        patch_get_mod_src(patch_id, p->param, i, &modsrc[i]);
+        modsrc[i] = patch_param_get_mod_src(patch_id, p->param, i);
 
         if (i < last_slot)
-            patch_get_mod_amt(patch_id, p->param, i, &modamt[i]);
+            modamt[i] = patch_param_get_mod_amt(patch_id, p->param, i);
 
         if (!mod_src_combo_get_iter_with_id(GTK_COMBO_BOX(p->mod_combo[i]),
                                                     modsrc[i], &moditer[i]))
@@ -504,13 +489,13 @@ get_mod_srcs:
     if (p->mod_only)
         goto set_mod_srcs;
 
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->param1), param1);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->param1), param1);
 
     if (p->param == PATCH_PARAM_PITCH)
-        phat_slider_button_set_value(PHAT_SLIDER_BUTTON(p->param2), param2);
+        phin_slider_button_set_value(PHIN_SLIDER_BUTTON(p->param2), param2);
 
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->key_track), ktrack);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->vel_sens), vsens);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->key_track), key_trk);
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->vel_sens),  vel_amt);
 
 set_mod_srcs:
 
@@ -520,14 +505,18 @@ set_mod_srcs:
                                                         &moditer[i]);
         if (p->param == PATCH_PARAM_PITCH)
         {
-            phat_slider_button_set_value(
-                                PHAT_SLIDER_BUTTON(p->mod_amount[i]),
+            phin_slider_button_set_value(
+                                PHIN_SLIDER_BUTTON(p->mod_amount[i]),
                                 modamt[i] * PATCH_MAX_PITCH_STEPS);
+            gtk_widget_set_sensitive(p->mod_amount[i],
+                                        modsrc[i] != MOD_SRC_NONE);
         }
         else if (i < last_slot)
         {
-            phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->mod_amount[i]),
+            phin_fan_slider_set_value(PHIN_FAN_SLIDER(p->mod_amount[i]),
                                                             modamt[i]);
+            gtk_widget_set_sensitive(p->mod_amount[i],
+                                        modsrc[i] != MOD_SRC_NONE);
         }
     }
 
diff --git a/src/gui/mod_section.h b/gui/mod_section.h
similarity index 100%
rename from src/gui/mod_section.h
rename to gui/mod_section.h
diff --git a/src/gui/mod_src_gui.c b/gui/mod_src_gui.c
similarity index 90%
rename from src/gui/mod_src_gui.c
rename to gui/mod_src_gui.c
index e86bc29..5a6f187 100644
--- a/src/gui/mod_src_gui.c
+++ b/gui/mod_src_gui.c
@@ -23,7 +23,8 @@
 
 
 #include "mod_src_gui.h"
-#include <phat/phat.h>
+
+#include "phin.h"
 
 #include "gui.h"
 #include "petri-foo.h"
@@ -106,19 +107,36 @@ GtkWidget* mod_src_new_pitch_adjustment(void)
 {
     GtkWidget* amt;
 
-    amt = phat_slider_button_new_with_range(12,    -PATCH_MAX_PITCH_STEPS,
+    amt = phin_slider_button_new_with_range(12,    -PATCH_MAX_PITCH_STEPS,
                                                     PATCH_MAX_PITCH_STEPS,
                                                     0.1, 1.0);
-
-    phat_slider_button_set_format(PHAT_SLIDER_BUTTON(amt),
+    phin_slider_button_set_format(PHIN_SLIDER_BUTTON(amt),
                                                     -1, NULL, NULL);
     gtk_widget_set_tooltip_text(amt, "Semitones");
-    phat_slider_button_set_threshold(PHAT_SLIDER_BUTTON(amt),
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(amt),
                                                             GUI_THRESHOLD);
     return amt;
 }
 
 
+int mod_src_combo_get_mod_src_id(GtkComboBox* combo)
+{
+    GtkTreeIter iter;
+
+    if (gtk_combo_box_get_active_iter(combo, &iter))
+    {
+        int     id;
+
+        gtk_tree_model_get( gtk_combo_box_get_model(combo),
+                            &iter, COLUMN_INT, &id, -1);
+
+        return id;
+    }
+
+    return MOD_SRC_NONE;
+}
+
+
 gboolean mod_src_callback_helper(int patch_id,          int slot,
                                     GtkComboBox* combo, PatchParamType par)
 {
@@ -137,10 +155,12 @@ gboolean mod_src_callback_helper(int patch_id,          int slot,
                patch_id,   slot, mod_src_name(id), id);
 
         /* FIXME: probably should check return value */
-        patch_set_mod_src(patch_id, par, slot, id);
+        patch_param_set_mod_src(patch_id, par, slot, id);
+
+        return id != MOD_SRC_NONE;
     }
 
-    return TRUE;
+    return FALSE;
 }
 
 
@@ -170,9 +190,11 @@ gboolean mod_src_callback_helper_lfo(int patch_id,
                     input_no);
             return FALSE;
         }
+
+        return id != MOD_SRC_NONE;
     }
 
-    return TRUE;
+    return FALSE;
 }
 
 
diff --git a/src/gui/mod_src_gui.h b/gui/mod_src_gui.h
similarity index 94%
rename from src/gui/mod_src_gui.h
rename to gui/mod_src_gui.h
index c92f274..694a5bb 100644
--- a/src/gui/mod_src_gui.h
+++ b/gui/mod_src_gui.h
@@ -35,11 +35,14 @@ enum {
 GtkWidget*      mod_src_new_combo_with_cell();
 
 /*  mod_src_new_pitch_adjustment
-        creates a phat slider button with semitones label within.
+        creates a phin slider button with semitones label within.
 */
 GtkWidget*      mod_src_new_pitch_adjustment(void);
 
 
+int mod_src_combo_get_mod_src_id(GtkComboBox* combo);
+
+
 /*  mod_src_callback_helper
 
         for use in the mod src combo box callback, it reads the
diff --git a/src/gui/paramtab.c b/gui/paramtab.c
similarity index 97%
rename from src/gui/paramtab.c
rename to gui/paramtab.c
index e734b3a..c845ddc 100644
--- a/src/gui/paramtab.c
+++ b/gui/paramtab.c
@@ -21,9 +21,10 @@
     This file is a derivative of a Specimen original, modified 2011
 */
 
-
+#include <assert.h>
 #include <gtk/gtk.h>
-#include <phat/phat.h>
+
+#include "phin.h"
 
 #include "paramtab.h"
 #include "gui.h"
@@ -104,7 +105,7 @@ void param_tab_set_param(ParamTab* self, PatchParamType param)
         break;
 
     default:
-        debug ("unrecognised parameter for param tab\n");
+        assert(0);
         return;
     }
 
@@ -115,6 +116,7 @@ void param_tab_set_param(ParamTab* self, PatchParamType param)
         gtk_box_pack_start(box, p->modsect1, FALSE, FALSE, 0);
         gtk_widget_show(p->modsect1);
     }
+    else {debug("param1 invalid\n");}
 
     if (ms2 != PATCH_PARAM_INVALID)
     {
diff --git a/src/gui/paramtab.h b/gui/paramtab.h
similarity index 100%
rename from src/gui/paramtab.h
rename to gui/paramtab.h
diff --git a/src/gui/patchlist.c b/gui/patchlist.c
similarity index 97%
rename from src/gui/patchlist.c
rename to gui/patchlist.c
index da59210..3f51759 100644
--- a/src/gui/patchlist.c
+++ b/gui/patchlist.c
@@ -140,7 +140,7 @@ void popup_menu(GdkEventButton *event, gboolean full_menu, gpointer data)
 static gboolean
 pressed_cb(GtkWidget *treeview, GdkEventButton *event, gpointer data)
 {
-    (void)data;
+    PatchListPrivate* p = (PatchListPrivate*)data;
 
     if (event->type == GDK_BUTTON_PRESS && event->button == 3)
     {
@@ -160,7 +160,10 @@ pressed_cb(GtkWidget *treeview, GdkEventButton *event, gpointer data)
                                                     &path,
                                                     NULL, NULL, NULL))
             {
+                g_signal_handlers_block_by_func(p->select, select_cb, p);
                 gtk_tree_selection_unselect_all(selection);
+                g_signal_handlers_unblock_by_func(p->select, select_cb, p);
+
                 gtk_tree_selection_select_path(selection, path);
                 full_menu = TRUE;
             }
diff --git a/src/gui/patchlist.h b/gui/patchlist.h
similarity index 100%
rename from src/gui/patchlist.h
rename to gui/patchlist.h
diff --git a/src/gui/patchsection.c b/gui/patchsection.c
similarity index 99%
rename from src/gui/patchsection.c
rename to gui/patchsection.c
index c820b85..efb2809 100644
--- a/src/gui/patchsection.c
+++ b/gui/patchsection.c
@@ -24,8 +24,10 @@
 
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
-#include <phat/phat.h>
 #include <stdlib.h>
+
+#include "phin.h"
+
 #include "patchsection.h"
 #include "petri-foo.h"
 #include "gui.h"
diff --git a/src/gui/patchsection.h b/gui/patchsection.h
similarity index 100%
rename from src/gui/patchsection.h
rename to gui/patchsection.h
diff --git a/src/petri-foo.c b/gui/petri-foo.c
similarity index 54%
rename from src/petri-foo.c
rename to gui/petri-foo.c
index 3bef391..d233bcd 100644
--- a/src/petri-foo.c
+++ b/gui/petri-foo.c
@@ -29,7 +29,7 @@
 
 #include "instance.h"
 #include "petri-foo.h"
-#include "gui/gui.h"
+#include "gui.h"
 #include "midi.h"
 #include "driver.h"
 #include "lfo.h"
@@ -40,64 +40,73 @@
 #include "names.h"
 #include "dish_file.h"
 #include "jackdriver.h"
+#include "global_settings.h"
+#include "msg_log.h"
+
 
 void show_usage (void)
 {
-	printf ("Usage: petri-foo [options] [bankname]\n\n");
-	printf ("Options:\n");
-	printf("  -n, --name <name>  Specify instance name, defaults to \"petri-foo\"\n" );
-	printf("  -h, --help         Display this help message\n\n");
-	printf ("For more information, please see:\n");
-	printf ("http://petri-foo.sourceforge.net/\n");
+    printf ("Usage: petri-foo [options] [bank]\n");
+    printf("(Bank files use .petri-foo extension)\n\n");
+
+    printf ("Options:\n");
+    printf("  -n, --name <name>  Specify instance name, "
+                                "defaults to \"petri-foo\"\n" );
+    printf("  -u, --unconnected  Don't auto-connect to JACK\n");
+    printf("  -h, --help         Display this help message\n\n");
+    printf ("For more information, please see:\n");
+    printf ("http://petri-foo.sourceforge.net/\n");
 }
 
 
 int main(int argc, char *argv[])
 {
-#ifdef HAVE_LASH
-	/*
-	 * Handle lash arguments here to prevent getopt_long() from
-	 * going bonkers over arguments it hasn't been told about.
-	 */
-	lash_args_t *lash_args = lash_extract_args(&argc, &argv);
-#endif
-
-	int opt;
-	int longopt_index;
-	static struct option long_options[] = 
-	{
-		{ "name", 1, 0, 'n'},
-		{ "uuid", 1, 0, 'U'},
-		{ "help", 0, 0, 'h'},
-		{ 0, 0, 0, 0}
-	};
-
-	/* command line argument processing */
-	while((opt = getopt_long(argc, argv, "n:U:h", long_options,
-	                         &longopt_index)) > 0)
-	{
-		switch (opt)
-		{
-			case 'n':
-				set_instance_name(optarg);
-				break;
-			case 'U':
-				jackdriver_set_uuid(strdup(optarg) );
-				break;
-			case 'h':
-				show_usage();
-				return 0;
-				break;
-			default:
-				show_usage();
-				return 1;
-				break;
-		}
-	}
+    int opt;
+    int longopt_index;
+
+    static struct option long_options[] =
+    {
+        { "name",           1, 0, 'n'},
+        { "unconnected",    0, 0, 'u'},
+        { "uuid",           1, 0, 'U'},
+        { "help",           0, 0, 'h'},
+        { 0, 0, 0, 0}
+    };
+
+
+    while((opt = getopt_long(argc, argv, "n:u:U:h",
+                                        long_options, &longopt_index)) > 0)
+    {
+        switch (opt)
+        {
+        case 'n':
+            set_instance_name(optarg);
+            break;
+
+        case 'u':
+            jackdriver_set_unconnected();
+            break;
+
+        case 'U':
+            jackdriver_set_uuid(strdup(optarg) );
+            break;
+
+        case 'h':
+            show_usage();
+            return 0;
+
+        default:
+            show_usage();
+            return 1;
+        }
+    }
 
     mod_src_create();
     gtk_init(&argc, &argv);
+
+    settings_init();
     gui_init();
+
     driver_init();
     lfo_tables_init();
     mixer_init();
@@ -122,10 +131,11 @@ int main(int argc, char *argv[])
     driver_stop();
     patch_shutdown();
     mixer_shutdown();
+    settings_write();
+    settings_free();
     free_instance_name();
     mod_src_destroy();
-
-    debug("Goodbye.\n");
+    msg_log(MSG_MESSAGE, "Goodbye!\n");
 
     return 0;
 }
diff --git a/gui/sample-editor.c b/gui/sample-editor.c
new file mode 100644
index 0000000..457cf0b
--- /dev/null
+++ b/gui/sample-editor.c
@@ -0,0 +1,721 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a Specimen original, modified 2011
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "petri-foo.h"
+#include "patch_set_and_get.h"
+#include "waveform.h"
+#include "gui.h"
+#include "mixer.h"
+#include "sample-editor.h"
+
+#include "basic_combos.h"
+
+enum
+{
+     ZOOM_MIN = 1,
+     ZOOM_MAX = 100
+};
+
+typedef struct _SampleEditorPrivate SampleEditorPrivate;
+
+
+struct _SampleEditorPrivate
+{
+    GtkWidget*  window;
+    GtkWidget*  toolbar1;
+    GtkWidget*  toolbar2;
+    GtkWidget*  waveform;
+    GtkWidget*  hscroll;
+    GtkObject*  hscrolladj;
+    GtkWidget*  fade_spin;
+    GtkWidget*  xfade_spin;
+    GtkWidget*  mark_combo;
+    GtkWidget*  mark_spin;
+    GtkWidget*  mark_val;
+    GtkWidget*  reset;
+
+    gboolean ignore_callback;
+    gboolean ignore_mark_change;
+
+    float   zoom_pc;
+    float   range;
+
+    int     old_play_start;
+    int     old_play_stop;
+
+    int     old_loop_start;
+    int     old_loop_stop;
+    int     old_fade;
+    int     old_xfade;
+
+    int     patch;
+};
+
+
+GtkWidget*  wf_thumb = 0;
+
+
+SampleEditorPrivate* sample_editor_private_new()
+{
+    SampleEditorPrivate* p = malloc(sizeof(*p));
+
+    if (!p)
+        return 0;
+
+    p->window =     0;
+    p->toolbar1 =   0;
+    p->toolbar2 =   0;
+    p->waveform =   0;
+    p->hscroll =    0;
+    p->hscrolladj = 0;
+    p->fade_spin =  0;
+    p->xfade_spin = 0;
+    p->mark_combo = 0;
+    p->mark_spin =  0;
+    p->mark_val =   0;
+    p->reset =      0;
+
+    p->ignore_callback =    FALSE;
+    p->ignore_mark_change = FALSE;
+
+    p->zoom_pc =    1.0;
+    p->range =      1.0;
+
+    p->old_play_start = 0;
+    p->old_play_stop =  0;
+
+    p->old_loop_start = 0;
+    p->old_loop_stop =  0;
+    p->old_fade =       0;
+    p->old_xfade =      0;
+
+    p->patch = -1;
+
+    return p;
+}
+
+
+static SampleEditorPrivate* se = 0;
+
+
+static void zoom_adj(void);
+static void update_mark_spin(void);
+static void update_fade_spins(void);
+static void cb_mark_combo_changed(GtkWidget* combo, gpointer data);
+
+static void cb_close(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    mixer_note_off_with_id(se->patch, patch_get_root_note(se->patch));
+    gtk_widget_hide(se->window);
+}
+
+static void cb_play(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    mixer_note_off_with_id(se->patch, patch_get_root_note(se->patch));
+    mixer_note_on_with_id(se->patch, patch_get_root_note(se->patch), 1.0);
+}
+
+static void cb_stop(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    mixer_note_off_with_id(se->patch, patch_get_root_note(se->patch));
+}
+
+static void cb_reset(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    patch_set_mark_frame(se->patch, WF_MARK_PLAY_START, se->old_play_start);
+    patch_set_mark_frame(se->patch, WF_MARK_PLAY_STOP,  se->old_play_stop);
+    patch_set_mark_frame(se->patch, WF_MARK_LOOP_START, se->old_loop_start);
+    patch_set_mark_frame(se->patch, WF_MARK_LOOP_STOP,  se->old_loop_stop);
+    cb_mark_combo_changed(se->mark_combo, 0);
+    gtk_widget_queue_draw(se->waveform);
+    gtk_widget_queue_draw(wf_thumb);
+}
+
+static void cb_clear(GtkWidget* widget, gpointer data)
+{
+    (void)widget;
+    char *op = data;
+
+    if (strcmp(op, "loop") == 0)
+    {
+        int play_start = patch_get_mark_frame(se->patch,WF_MARK_PLAY_START);
+        int play_stop = patch_get_mark_frame(se->patch, WF_MARK_PLAY_STOP);
+        patch_set_mark_frame(se->patch, WF_MARK_LOOP_START, play_start);
+        patch_set_mark_frame(se->patch, WF_MARK_LOOP_STOP, play_stop);
+     }
+     else if (strcmp(op, "play") == 0)
+     {
+        int frames = patch_get_mark_frame(se->patch, WF_MARK_STOP);
+        patch_set_mark_frame(se->patch, WF_MARK_PLAY_START, 0);
+        patch_set_mark_frame(se->patch, WF_MARK_PLAY_STOP, frames - 1);
+     }
+
+    cb_mark_combo_changed(se->mark_combo, 0);
+    gtk_widget_queue_draw(se->waveform);
+    gtk_widget_queue_draw(wf_thumb);
+}
+
+
+static void update_mark_val(int val)
+{
+    const float* wav = patch_get_sample(se->patch);
+    char buf[40];
+    snprintf(buf, 40, "%2.6f", wav[val * 2]);
+    gtk_label_set_label(GTK_LABEL(se->mark_val), buf);
+}
+
+
+static void cb_scroll(GtkWidget* scroll, gpointer data)
+{
+    (void)data;
+    float val = gtk_range_get_value(GTK_RANGE(scroll));
+    waveform_set_range(WAVEFORM(se->waveform), val, val + se->range);
+}
+
+
+static void cb_mark_spin_changed(GtkWidget* spin, gpointer data)
+{
+    (void)spin;(void)data;
+    int val;
+    int mark;
+
+    val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(se->mark_spin));
+    mark = waveform_get_mark(WAVEFORM(se->waveform));
+
+    patch_set_mark_frame(se->patch, mark, val);
+    update_mark_val(val);
+    update_fade_spins(); /* so xfade spin button range is set correctly */
+    gtk_widget_queue_draw(se->waveform);
+    gtk_widget_queue_draw(wf_thumb);
+}
+
+
+static void cb_fade_spin_changed(GtkWidget* spin, gpointer data)
+{
+    (void)spin;(void)data;
+    int val = gtk_spin_button_get_value_as_int(
+                                        GTK_SPIN_BUTTON(se->fade_spin));
+    patch_set_fade_samples(se->patch, val);
+}
+
+
+static void cb_xfade_spin_changed(GtkWidget* spin, gpointer data)
+{
+    (void)spin;(void)data;
+    int val = gtk_spin_button_get_value_as_int(
+                                        GTK_SPIN_BUTTON(se->xfade_spin));
+    patch_set_xfade_samples(se->patch, val);
+    update_mark_spin(); /* so mark spin range is set correctly */
+}
+
+
+static void update_mark_spin(void)
+{
+    int min;
+    int max;
+
+    int mark = waveform_get_mark(WAVEFORM(se->waveform));
+    int val =  patch_get_mark_frame_range(se->patch, mark, &min, &max);
+
+    if (val < 0)
+    {
+        val = patch_get_mark_frame(se->patch, mark);
+        gtk_widget_set_sensitive(se->mark_spin, FALSE);
+    }
+    else
+        gtk_widget_set_sensitive(se->mark_spin, TRUE);
+
+    g_signal_handlers_block_by_func(se->mark_spin, cb_mark_spin_changed, 0);
+    gtk_spin_button_set_range(GTK_SPIN_BUTTON(se->mark_spin), min, max);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(se->mark_spin), val);
+    g_signal_handlers_unblock_by_func(se->mark_spin,cb_mark_spin_changed,0);
+
+    update_fade_spins(); /* ie set the range of xfade */
+    update_mark_val(val);
+}
+
+
+static void update_fade_spins(void)
+{
+    int fade =      patch_get_fade_samples(se->patch);
+    int max_fade =  patch_get_max_fade_samples(se->patch);
+    int xfade =     patch_get_xfade_samples(se->patch);
+    int max_xfade = patch_get_max_xfade_samples(se->patch);
+
+    g_signal_handlers_block_by_func(se->fade_spin, cb_fade_spin_changed, 0);
+    gtk_spin_button_set_range(GTK_SPIN_BUTTON(se->fade_spin), 0, max_fade);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(se->fade_spin), fade);
+    g_signal_handlers_unblock_by_func(  se->fade_spin,
+                                        cb_fade_spin_changed,
+                                        0);
+
+    g_signal_handlers_block_by_func(se->xfade_spin,
+                                    cb_xfade_spin_changed,
+                                    0);
+
+    gtk_spin_button_set_range(GTK_SPIN_BUTTON(se->xfade_spin), 0,
+                                                            max_xfade);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(se->xfade_spin), xfade);
+    g_signal_handlers_unblock_by_func(se->xfade_spin,
+                                                cb_xfade_spin_changed, 0);
+}
+
+
+static void cb_mark_combo_changed(GtkWidget* combo, gpointer data)
+{
+    (void)combo;(void)data;
+    se->ignore_mark_change = TRUE;
+    waveform_set_mark(WAVEFORM(se->waveform),
+                gtk_combo_box_get_active(GTK_COMBO_BOX(se->mark_combo)));
+    update_mark_spin();
+}
+
+static void cb_wf_play_changed(void)
+{
+    /*  there's some chance the play point that was changed is
+        selected and shown in the mark spin. */
+    update_mark_spin();
+    update_fade_spins();
+    gtk_widget_queue_draw(wf_thumb);
+}
+
+static void cb_wf_loop_changed(void)
+{
+    /*  there's some chance the play point that was changed is
+        selected and shown in the mark spin. */
+    update_mark_spin();
+    update_fade_spins();
+    gtk_widget_queue_draw(wf_thumb);
+}
+
+static void cb_wf_mark_changed(void)
+{
+    /* ditto cb_wf_play_changed */
+    if (se->ignore_mark_change)
+    {
+        se->ignore_mark_change = FALSE;
+        return;
+    }
+    gtk_combo_box_set_active(GTK_COMBO_BOX(se->mark_combo),
+                                waveform_get_mark(WAVEFORM(se->waveform)));
+}
+
+static void cb_wf_view_changed(void)
+{
+    float start, stop;
+    waveform_get_range(WAVEFORM(se->waveform), &start, &stop);
+
+    g_signal_handlers_block_by_func(se->hscroll, cb_scroll, 0);
+    zoom_adj();
+    g_signal_handlers_unblock_by_func(se->hscroll, cb_scroll, 0);
+
+}
+
+
+static void zoom_adj(void)
+{
+    float page_size=0;
+    float step_inc=0;
+    float page_inc=0;
+    float val=0;
+    float start=0;
+    float stop=0;
+
+    waveform_get_range(WAVEFORM(se->waveform), &start, &stop);
+    gtk_range_set_range(GTK_RANGE(se->hscroll), start, stop);
+
+    val = start;
+    se->range = page_size = stop - start;
+
+    step_inc = se->range / 100;
+    page_inc = step_inc / 10;
+    se->hscrolladj = gtk_adjustment_new(val, 0.0, 1.0,
+                                            step_inc,
+                                            page_inc,
+                                            page_size);
+
+    gtk_range_set_adjustment(GTK_RANGE(se->hscroll),
+                                            GTK_ADJUSTMENT(se->hscrolladj));
+
+     /* emit value-changed signal so waveform is redrawn with
+      * the new dimensions */
+    g_signal_emit_by_name(G_OBJECT(se->hscroll), "value-changed");
+
+    return;
+}
+
+static void cb_zoom_1to1(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    int frames;
+    int width;
+    float zoom_level;
+    float zoom_multiplier;
+
+    waveform_get_size(WAVEFORM(se->waveform), &width, NULL);
+    frames = patch_get_frames(se->patch);
+
+    zoom_level = width / (double)frames;
+
+    zoom_multiplier = se->range / zoom_level;
+
+    waveform_zoom(WAVEFORM(se->waveform), zoom_multiplier, 0.5);
+    zoom_adj();
+}
+
+static void cb_zoom_all(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    waveform_set_range(WAVEFORM(se->waveform), 0.0, 1.0);
+    zoom_adj();
+}
+
+
+static void cb_zoom_in(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    waveform_zoom(WAVEFORM(se->waveform), 2.0, 0.5);
+    zoom_adj();
+}
+
+static void cb_zoom_out(GtkWidget* widget, gpointer data)
+{
+    (void)widget;(void)data;
+    waveform_zoom(WAVEFORM(se->waveform), 0.5, 0.5);
+    zoom_adj();
+}
+
+
+static void sensitize_toolbars(gboolean tools_active)
+{
+    gtk_widget_set_sensitive(se->toolbar1, tools_active);
+    gtk_widget_set_sensitive(se->toolbar2, tools_active);
+    gtk_widget_set_sensitive(se->reset,    tools_active);
+}
+
+
+void sample_editor_show(int id)
+{
+    se->patch = id;
+
+    waveform_set_patch(WAVEFORM(se->waveform), se->patch);
+
+    if (patch_get_sample(se->patch))
+    {
+        se->old_play_start = patch_get_mark_frame(se->patch,
+                                                WF_MARK_PLAY_START);
+        se->old_play_stop =  patch_get_mark_frame(se->patch,
+                                                WF_MARK_PLAY_STOP);
+        se->old_loop_start = patch_get_mark_frame(se->patch,
+                                                WF_MARK_LOOP_START);
+        se->old_loop_stop =  patch_get_mark_frame(se->patch,
+                                                WF_MARK_LOOP_STOP);
+        sensitize_toolbars(true);
+    }
+    else
+        sensitize_toolbars(false);
+
+    if (gtk_combo_box_get_active(GTK_COMBO_BOX(se->mark_combo))
+        == WF_MARK_START)
+    {
+        gtk_combo_box_set_active(GTK_COMBO_BOX(se->mark_combo),
+                                                WF_MARK_PLAY_START);
+    }
+
+    update_fade_spins();
+    gtk_widget_show(se->window);
+}
+
+
+void sample_editor_hide(void)
+{
+    gtk_widget_hide(se->window);
+}
+
+
+gboolean sample_editor_get_visible(void)
+{
+    return gtk_widget_get_visible(se->window);
+}
+
+
+
+void sample_editor_update(void)
+{
+    if (se->patch < 0)
+        return;
+
+    if (patch_get_sample(se->patch))
+        sensitize_toolbars(true);
+    else
+        sensitize_toolbars(false);
+
+    waveform_set_range(WAVEFORM(se->waveform), 0.0, 1.0);
+    gtk_widget_queue_draw(se->waveform);
+}
+
+
+
+void sample_editor_set_thumb(GtkWidget* thumb)
+{
+    wf_thumb = thumb;
+}
+
+
+void sample_editor_init(GtkWidget * parent)
+{
+    GtkWindow* w;
+    GtkWidget* master_vbox;
+    GtkWidget* hbox;
+    GtkWidget* brow;
+    GtkWidget* button;
+    GtkWidget* image;
+    GtkWidget* label;
+    GtkWidget* tmp;
+
+    assert(se == 0);
+
+    se = sample_editor_private_new();
+
+    /* main window */
+    se->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    w = GTK_WINDOW(se->window);
+    gtk_window_set_title(w, "Edit Sample");
+    gtk_window_set_resizable(w, TRUE);
+    gtk_window_set_transient_for(w, GTK_WINDOW(parent));
+    gtk_window_set_modal(w, FALSE);
+    g_signal_connect(G_OBJECT(w), "delete-event", G_CALLBACK(cb_close),
+                                                                   NULL);
+
+    /* master vbox */
+    master_vbox = gtk_vbox_new(FALSE, GUI_SPACING);
+    gtk_container_add(GTK_CONTAINER(se->window), master_vbox);
+    gtk_container_set_border_width(GTK_CONTAINER(se->window), GUI_SPACING);
+    gtk_widget_show(master_vbox);
+
+    /* top row hbox */
+    se->toolbar1 = hbox = gtk_hbox_new(FALSE, GUI_SPACING);
+    gtk_box_pack_start(GTK_BOX(master_vbox), hbox, FALSE, FALSE, 0);
+    gtk_widget_show(hbox);
+
+    /* play button */
+    image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY,
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+    button = gtk_button_new();
+    gtk_container_add(GTK_CONTAINER(button), image);
+    gtk_box_pack_start(GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect_swapped(G_OBJECT (button), "clicked",
+                                    G_CALLBACK(cb_play), NULL);
+    gtk_widget_show(image);
+    gtk_widget_show(button);
+
+    /* stop button */
+    image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_STOP,
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+    button = gtk_button_new ( );
+    gtk_container_add (GTK_CONTAINER (button), image);
+    gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect_swapped (G_OBJECT (button), "clicked",
+                                    G_CALLBACK (cb_stop), NULL);
+    gtk_widget_show(image);
+    gtk_widget_show(button);
+
+    /* separator */
+    tmp = gtk_vseparator_new();
+    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
+    gtk_widget_show(tmp);
+
+    /* fade spin button */
+    label = gtk_label_new("Fade:");
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+    gtk_widget_show(label);
+
+    se->fade_spin = gtk_spin_button_new_with_range(0, 1, 1);
+    gtk_box_pack_start(GTK_BOX(hbox), se->fade_spin, FALSE, FALSE, 0);
+    gtk_widget_show(se->fade_spin);
+    g_signal_connect(G_OBJECT(se->fade_spin), "value-changed",
+                            G_CALLBACK(cb_fade_spin_changed), NULL);
+
+    /* X-fade spin button */
+    label = gtk_label_new("X-Fade:");
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+    gtk_widget_show(label);
+
+    se->xfade_spin = gtk_spin_button_new_with_range(0, 1, 1);
+    gtk_box_pack_start(GTK_BOX(hbox), se->xfade_spin, FALSE, FALSE, 0);
+    gtk_widget_show(se->xfade_spin);
+    g_signal_connect(G_OBJECT(se->xfade_spin), "value-changed",
+                            G_CALLBACK(cb_xfade_spin_changed), NULL);
+
+    /* separator */
+    tmp = gtk_vseparator_new();
+    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
+    gtk_widget_show(tmp);
+
+    /* mark combo */
+    se->mark_combo = basic_combo_create(waveform_get_mark_names());
+    gtk_box_pack_start(GTK_BOX(hbox), se->mark_combo, FALSE, FALSE, 0);
+    gtk_widget_show(se->mark_combo);
+    g_signal_connect(G_OBJECT(se->mark_combo), "changed",
+                            G_CALLBACK(cb_mark_combo_changed), NULL);
+
+    se->mark_spin = gtk_spin_button_new_with_range(0, 1, 1);
+    gtk_box_pack_start(GTK_BOX(hbox), se->mark_spin, FALSE, FALSE, 0);
+    gtk_widget_show(se->mark_spin);
+    g_signal_connect(G_OBJECT(se->mark_spin), "value-changed",
+                            G_CALLBACK(cb_mark_spin_changed), NULL);
+
+    /* mark spin value label */
+    se->mark_val = gtk_label_new("");
+    gtk_box_pack_start(GTK_BOX(hbox), se->mark_val, FALSE, FALSE, 0);
+    gtk_widget_show(se->mark_val);
+
+    /* separator */
+    tmp = gtk_vseparator_new();
+    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
+    gtk_widget_show(tmp);
+
+    /* loop points clear button */
+    button = gtk_button_new_with_label ("Loop");
+    gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_clear),
+                                                        (gpointer) "loop");
+    gtk_widget_show (button);
+
+    /* play points clear button */
+    button = gtk_button_new_with_label ("Play");
+    gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_clear),
+                                                        (gpointer) "play");
+    gtk_widget_show (button);
+
+
+    /* clear label */
+    label = gtk_label_new ("Clear Points:");
+    gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+    gtk_widget_show (label);
+
+    /* waveform */
+    se->waveform = waveform_new();
+    waveform_set_patch(        WAVEFORM(se->waveform), -1);
+    waveform_set_size(         WAVEFORM(se->waveform),  512, 256);
+    waveform_set_interactive(  WAVEFORM(se->waveform), TRUE);
+    gtk_box_pack_start(GTK_BOX(master_vbox), se->waveform, TRUE, TRUE, 0);
+    gtk_widget_show(se->waveform);
+    g_signal_connect(G_OBJECT(se->waveform), "play-changed",
+                            G_CALLBACK(cb_wf_play_changed), NULL);
+    g_signal_connect(G_OBJECT(se->waveform), "loop-changed",
+                            G_CALLBACK(cb_wf_loop_changed), NULL);
+    g_signal_connect(G_OBJECT(se->waveform), "mark-changed",
+                            G_CALLBACK(cb_wf_mark_changed), NULL);
+    g_signal_connect(G_OBJECT(se->waveform), "view-changed",
+                            G_CALLBACK(cb_wf_view_changed), NULL);
+
+    /* waveform scrollbar */
+    se->hscrolladj = gtk_adjustment_new(0.0, 0.0, 1.0, 0.0, 0.0, 1.0);
+    se->hscroll = gtk_hscrollbar_new(GTK_ADJUSTMENT(se->hscrolladj));
+    gtk_box_pack_start(GTK_BOX(master_vbox), se->hscroll, FALSE, FALSE, 0);
+    g_signal_connect(G_OBJECT(se->hscroll), "value-changed",
+                                            G_CALLBACK (cb_scroll), NULL);
+    gtk_widget_show (se->hscroll);
+
+    /* bottom row hbox */
+    brow = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start (GTK_BOX(master_vbox), brow, FALSE, FALSE, 0);
+    gtk_widget_show(brow);
+
+    se->toolbar2 = hbox = gtk_hbox_new (FALSE, GUI_SPACING);
+    gtk_box_pack_start (GTK_BOX(brow), hbox, FALSE, FALSE, 0);
+    gtk_widget_show (hbox);
+
+    /* zoom-out button */
+    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_OUT,
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+    button = gtk_button_new();
+    gtk_container_add(GTK_CONTAINER(button), image);
+    gtk_box_pack_start(GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect_swapped(G_OBJECT (button), "clicked",
+                                    G_CALLBACK(cb_zoom_out), NULL);
+    gtk_widget_show(image);
+    gtk_widget_show(button);
+
+    /* zoom-in button */
+    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_IN,
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+    button = gtk_button_new();
+    gtk_container_add(GTK_CONTAINER(button), image);
+    gtk_box_pack_start(GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect_swapped(G_OBJECT (button), "clicked",
+                                    G_CALLBACK(cb_zoom_in), NULL);
+    gtk_widget_show(image);
+    gtk_widget_show(button);
+
+    /* zoom-1:1 button */
+    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_100,
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+    button = gtk_button_new();
+    gtk_container_add(GTK_CONTAINER(button), image);
+    gtk_box_pack_start(GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect_swapped(G_OBJECT (button), "clicked",
+                                    G_CALLBACK(cb_zoom_1to1), NULL);
+    gtk_widget_show(image);
+    gtk_widget_show(button);
+
+    /* zoom-all button */
+    image = gtk_image_new_from_stock(GTK_STOCK_ZOOM_FIT,
+                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
+    button = gtk_button_new();
+    gtk_container_add(GTK_CONTAINER(button), image);
+    gtk_box_pack_start(GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect_swapped(G_OBJECT (button), "clicked",
+                                    G_CALLBACK(cb_zoom_all), NULL);
+    gtk_widget_show(image);
+    gtk_widget_show(button);
+
+    hbox = gtk_hbox_new (FALSE, GUI_SPACING);
+    gtk_box_pack_end(GTK_BOX(brow), hbox, FALSE, FALSE, 0);
+    gtk_widget_show (hbox);
+
+     /* close button */
+    button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+    gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(cb_close),
+                                                                    NULL);
+    gtk_widget_show (button);
+
+    /* reset button */
+    se->reset = button = gtk_button_new_with_label ("Reset");
+    gtk_box_pack_end(GTK_BOX (hbox), button, FALSE, FALSE, 0);
+    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(cb_reset),
+                                                                    NULL);
+    gtk_widget_show (button);
+}
diff --git a/src/gui/sample-editor.h b/gui/sample-editor.h
similarity index 78%
rename from src/gui/sample-editor.h
rename to gui/sample-editor.h
index d2327de..ecada79 100644
--- a/src/gui/sample-editor.h
+++ b/gui/sample-editor.h
@@ -28,14 +28,15 @@
 #include <gtk/gtk.h>
 
 
-void sample_editor_init(GtkWidget* parent);
+void        sample_editor_init(GtkWidget* parent);
 
 /* waveform thumb in sample tab */
-void sample_editor_set_thumb(GtkWidget* thumb);
+void        sample_editor_set_thumb(GtkWidget* thumb);
 
-void sample_editor_show(int id);
-
-void sample_editor_update(void);
+void        sample_editor_show(int id);
+void        sample_editor_hide(void);
+gboolean    sample_editor_get_visible(void);
+void        sample_editor_update(void);
 
 
 #endif /* __SAMPLE_EDITOR_H__ */
diff --git a/src/gui/sample-selector.c b/gui/sample-selector.c
similarity index 79%
rename from src/gui/sample-selector.c
rename to gui/sample-selector.c
index d7e725d..8c079f4 100644
--- a/src/gui/sample-selector.c
+++ b/gui/sample-selector.c
@@ -31,12 +31,15 @@
 #include "basic_combos.h"
 #include "gui.h"
 #include "petri-foo.h"
+#include "pf_error.h"
 #include "mixer.h"
+#include "msg_log.h"
 #include "patch_set_and_get.h"
 #include "patch_util.h"
 #include "names.h"
 #include "sample-selector.h"
 #include "sample.h"
+#include "global_settings.h"
 
 
 #include <sndfile.h>
@@ -54,7 +57,9 @@ typedef struct _raw_box
     GtkWidget* box;
 
     /* box contains: */
+    GtkWidget* toggle_box;
     GtkWidget* check;
+    GtkWidget* auto_preview;
     GtkWidget* table;
 
     /* table contains: */
@@ -95,22 +100,23 @@ static void cb_load(raw_box* rb)
     int err;
     char *name = (char *)
             gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(rb->dialog));
+    global_settings* settings = settings_get();
 
     if (!name)
         goto fail;
 
-    debug("about to load sample...\n");
-
     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rb->check)))
     {
         int samplerate = gtk_spin_button_get_value_as_int(
                                     GTK_SPIN_BUTTON(rb->samplerate));
 
-        err = patch_sample_load(patch, name,    samplerate,
+        if (patch_sample_load(patch, name,    samplerate,
                                                 rb->channels,
-                                                get_format(rb));
-        if (err)
+                                                get_format(rb)) < 0)
+        {
+            err = pf_error_get();
             goto fail;
+        }
     }
     else
     {   /* don't repeat load sample */
@@ -119,16 +125,32 @@ static void cb_load(raw_box* rb)
         if (s->filename && strcmp(name, s->filename) == 0)
             return;
 
-        if ((err = patch_sample_load(patch, name, 0, 0, 0)))
+        if (patch_sample_load(patch, name, 0, 0, 0))
+        {
+            err = pf_error_get();
             goto fail;
+        }
+    }
+
+    if (name)
+    {
+        char* dirname = g_path_get_dirname(name);
+
+        if (dirname)
+        {
+            if (settings->last_sample_dir)
+                free(settings->last_sample_dir);
+
+            settings->last_sample_dir = strdup(dirname);
+            free(dirname);
+        }
     }
 
-    debug ("Successfully loaded sample %s\n", name);
     return;
 
 fail:
     if (!name)
-    {
+    {   /* I don't really think this is possible, but hey. */
         errmsg("no file selected\n");
         msg = gtk_message_dialog_new(GTK_WINDOW(rb->dialog),
                                      GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -137,8 +159,12 @@ fail:
     }
     else
     {
-        errmsg ("Failed to load sample %s for patch %d (%s)\n", name,
-                                            patch, patch_strerror(err));
+        msg_log(MSG_TYPE_ERROR, /*  Unlike MSG_ERROR, MSG_TYPE_ERROR does
+                                    not set the log notification flag */
+                "Failed to load sample %s for patch %d (%s)\n",
+                name, patch, pf_error_str(err));
+
+        /* do our own notification here: */
         msg = gtk_message_dialog_new(GTK_WINDOW(rb->dialog),
                                      GTK_DIALOG_DESTROY_WITH_PARENT,
                                      GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
@@ -175,18 +201,17 @@ static void cb_preview(raw_box* rb)
 
 static void cb_cancel(void)
 {
-    debug("cancelling sample load...\n");
-
     if (!last_sample->filename)
     {
-        debug("no sample to restore, just unloading...\n");
         patch_sample_unload(patch);
         return;
     }
 
     if (strcmp(patch_get_sample_name(patch), last_sample->filename) != 0)
     {
-        debug("restoring sample:%s\n", last_sample->filename);
+        msg_log(MSG_MESSAGE, "Restoring sample:%s\n",
+                                last_sample->filename);
+
         patch_sample_load(patch, last_sample->filename,
                                  last_sample->raw_samplerate,
                                  last_sample->raw_channels,
@@ -204,34 +229,15 @@ static void raw_toggles_toggled_cb(GtkToggleButton* tog, gpointer data)
     if (gtk_toggle_button_get_active(tog))
     {
         if (tog == GTK_TOGGLE_BUTTON(rb->stereo))
-        {
-            debug("setting raw channels to two\n");
             rb->channels = 2;
-        }
         else if (tog == GTK_TOGGLE_BUTTON(rb->mono))
-        {
-            debug("setting raw channels to one\n");
             rb->channels = 1;
-        }
         else if (tog == GTK_TOGGLE_BUTTON(rb->big_endian))
-        {
-            debug("setting raw endianness to big\n");
             rb->endian = 2;
-        }
         else if (tog == GTK_TOGGLE_BUTTON(rb->little_endian))
-        {
-            debug("setting raw endianness to little\n");
             rb->endian = 1;
-        }
         else if (tog == GTK_TOGGLE_BUTTON(rb->file_endian))
-        {
-            debug("setting raw endianness to file\n");
             rb->endian = 0;
-        }
-        else
-        {
-            debug("unknown raw data toggle\n");
-        }
     }
 }
 
@@ -246,6 +252,17 @@ static void raw_toggled_cb(GtkToggleButton* raw_toggle, gpointer data)
 }
 
 
+static void selection_changed_cb(GtkFileChooser* dialog, gpointer data)
+{
+    (void)dialog;
+
+    raw_box* rb = (raw_box*)data;
+
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rb->auto_preview)))
+        cb_preview(rb);
+}
+
+
 static raw_box* raw_box_new(GtkWidget* dialog)
 {
     GtkTable* t;
@@ -264,13 +281,27 @@ static raw_box* raw_box_new(GtkWidget* dialog)
     rb->endian = 0;
 
     rb->box = gtk_vbox_new(FALSE, 0);
+    rb->toggle_box = gtk_hbox_new(FALSE,10);
 
     rb->check = gtk_check_button_new_with_label("Raw/Headerless");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb->check), FALSE);
     gtk_widget_show(rb->check);
-    gtk_box_pack_start(GTK_BOX(rb->box), rb->check, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(rb->toggle_box), rb->check, FALSE, FALSE, 0);
     g_signal_connect(GTK_OBJECT(rb->check), "toggled",
                                             G_CALLBACK(raw_toggled_cb), rb);
+
+    rb->auto_preview = gtk_check_button_new_with_label("Auto Preview");
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb->auto_preview),
+                                                                FALSE);
+    gtk_widget_show(rb->auto_preview);
+    gtk_box_pack_start(GTK_BOX(rb->toggle_box), rb->auto_preview, FALSE,
+                                                                FALSE, 0);
+    g_signal_connect(dialog, "selection-changed",
+                                    G_CALLBACK(selection_changed_cb), rb);
+
+    gtk_box_pack_start(GTK_BOX(rb->box), rb->toggle_box, TRUE, TRUE, 0);
+    gtk_widget_show(rb->toggle_box);
+
     rb->table = gtk_table_new(5, 2, FALSE);
     gtk_box_pack_start(GTK_BOX(rb->box), rb->table, TRUE, TRUE, 0);
 
@@ -329,6 +360,7 @@ int sample_selector_show(int id, GtkWidget* parent_window,
 {
     GtkWidget* dialog;
     raw_box* rawbox;
+    global_settings* settings = settings_get();
 
     enum {
         RESPONSE_LOAD = 1,
@@ -357,10 +389,17 @@ int sample_selector_show(int id, GtkWidget* parent_window,
     {
         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
                                 last_sample->filename);
+        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
+                         g_path_get_dirname(last_sample->filename));
+    } 
+    else {
+        if ( settings->last_sample_dir) 
+            gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
+                                          settings->last_sample_dir);
     }
 
     gtk_dialog_add_button(GTK_DIALOG(dialog),
-                                "Load", RESPONSE_LOAD);
+                                "Loa_d", RESPONSE_LOAD);
 
     gtk_dialog_add_button(GTK_DIALOG(dialog),
                                 "Pre_view", RESPONSE_PREVIEW);
diff --git a/src/gui/sample-selector.h b/gui/sample-selector.h
similarity index 100%
rename from src/gui/sample-selector.h
rename to gui/sample-selector.h
diff --git a/src/gui/sampletab.c b/gui/sampletab.c
similarity index 98%
rename from src/gui/sampletab.c
rename to gui/sampletab.c
index 2ecaa51..c0944e7 100644
--- a/src/gui/sampletab.c
+++ b/gui/sampletab.c
@@ -148,8 +148,6 @@ static void set_mode(SampleTabPrivate* p)
 
     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->reverse_check)))
         mode |= PATCH_PLAY_REVERSE;
-    else
-        mode |= PATCH_PLAY_FORWARD;
 
     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->to_end_check)))
         mode |= PATCH_PLAY_TO_END;
@@ -244,7 +242,7 @@ static GtkWidget* file_button_new(SampleTabPrivate* p)
     hbox = gtk_hbox_new(FALSE, 0);
     p->file_label = gtk_label_new("Load File");
     vsep = gtk_vseparator_new();
-    image = gtk_image_new_from_file(PIXMAPSDIR "open.png");
+    image = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
 
     gtk_box_pack_start(GTK_BOX(hbox), p->file_label, TRUE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(hbox), vsep, FALSE, FALSE, GUI_SPACING);
@@ -382,9 +380,10 @@ void sample_tab_set_patch(SampleTab* self, int patch)
             gtk_combo_box_set_active_iter(GTK_COMBO_BOX(p->mode_opt),
                                                                     &iter);
         }
+
+        update_to_end_check(p);
     }
 
-    update_to_end_check(p);
     unblock(p);
 }
 
diff --git a/src/gui/sampletab.h b/gui/sampletab.h
similarity index 100%
rename from src/gui/sampletab.h
rename to gui/sampletab.h
diff --git a/gui/voicetab.c b/gui/voicetab.c
new file mode 100644
index 0000000..64abd34
--- /dev/null
+++ b/gui/voicetab.c
@@ -0,0 +1,249 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a Specimen original, modified 2011
+*/
+
+
+#include <gtk/gtk.h>
+
+#include "phin.h"
+
+#include "voicetab.h"
+#include "gui.h"
+#include "patch_set_and_get.h"
+#include "bool_section.h"
+#include "float_section.h"
+
+
+typedef struct _VoiceTabPrivate VoiceTabPrivate;
+
+#define VOICE_TAB_GET_PRIVATE(obj)      \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+        VOICE_TAB_TYPE, VoiceTabPrivate))
+
+struct _VoiceTabPrivate
+{
+    int patch;
+    guint refresh;
+    GtkWidget* cut_sb;
+    GtkWidget* cutby_sb;
+
+    GtkWidget* mono_check;
+    GtkWidget* legato_sect;
+
+    GtkWidget* porta_sect;
+    GtkWidget* time_sect;
+};
+
+
+G_DEFINE_TYPE(VoiceTab, voice_tab, GTK_TYPE_VBOX);
+
+
+static void voice_tab_class_init(VoiceTabClass* klass)
+{
+    GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
+    voice_tab_parent_class = g_type_class_peek_parent(klass);
+    g_type_class_add_private(object_class, sizeof(VoiceTabPrivate));
+}
+
+
+static void cut_cb(PhinSliderButton* button, VoiceTabPrivate* p)
+{
+    int val = phin_slider_button_get_value(button);
+    patch_set_cut(p->patch, val);
+}
+
+
+static void cutby_cb(PhinSliderButton* button, VoiceTabPrivate* p)
+{
+    int val = phin_slider_button_get_value(button);
+    patch_set_cut_by(p->patch, val);
+}
+
+
+static void porta_cb(BoolSection* b, VoiceTabPrivate* p)
+{
+    if (bool_section_get_active(b))
+        gtk_widget_set_sensitive(p->time_sect, TRUE);
+    else
+        gtk_widget_set_sensitive(p->time_sect, FALSE);
+}
+
+
+static void mono_cb(GtkToggleButton* button, VoiceTabPrivate* p)
+{
+    patch_set_monophonic(p->patch, gtk_toggle_button_get_active(button));
+    gtk_widget_set_sensitive(p->legato_sect,
+                            gtk_toggle_button_get_active(button));
+}
+
+
+static void connect(VoiceTabPrivate* p)
+{
+    g_signal_connect(G_OBJECT(p->cut_sb), "value-changed",
+                        G_CALLBACK(cut_cb), (gpointer)p);
+    g_signal_connect(G_OBJECT(p->cutby_sb), "value-changed",
+                        G_CALLBACK(cutby_cb), (gpointer)p);
+
+    g_signal_connect(G_OBJECT(p->mono_check), "toggled",
+                        G_CALLBACK(mono_cb), (gpointer)p);
+
+    g_signal_connect(G_OBJECT(p->porta_sect), "toggled",
+                        G_CALLBACK(porta_cb), (gpointer)p);
+}
+
+
+static void block(VoiceTabPrivate* p)
+{
+    g_signal_handlers_block_by_func(p->cut_sb,      cut_cb,     p);
+    g_signal_handlers_block_by_func(p->cutby_sb,    cutby_cb,   p);
+    g_signal_handlers_block_by_func(p->mono_check,  mono_cb,    p);
+    g_signal_handlers_block_by_func(p->porta_sect,  porta_cb,   p);
+}
+
+
+static void unblock(VoiceTabPrivate* p)
+{
+    g_signal_handlers_unblock_by_func(p->cut_sb,        cut_cb,     p);
+    g_signal_handlers_unblock_by_func(p->cutby_sb,      cutby_cb,   p);
+    g_signal_handlers_unblock_by_func(p->mono_check,    mono_cb,    p);
+    g_signal_handlers_unblock_by_func(p->porta_sect,    porta_cb,   p);
+}
+
+
+static void voice_tab_init(VoiceTab* self)
+{
+    VoiceTabPrivate* p = VOICE_TAB_GET_PRIVATE(self);
+    GtkBox* box = GTK_BOX(self);
+    GtkWidget* table;
+    GtkTable* t;
+
+    int a1 = 0,     a2 = 1;
+    int b1 = 1,     b2 = 2;
+    int /*c1 = 2*/  c2 = 3;
+
+    int y = 0;
+
+    p->patch = -1;
+    p->refresh = -1;
+
+    gtk_container_set_border_width(GTK_CONTAINER(self), GUI_BORDERSPACE);
+
+    /* table */
+    table = gtk_table_new(8, 3, FALSE);
+    t = (GtkTable*) table;
+    gui_pack(box, table);
+
+    /* title */
+    gui_attach(t, gui_title_new("Voice"), a1, c2, y, y + 1);
+    ++y;
+
+    /* title padding  */
+    gui_attach(t, gui_vpad_new(GUI_TITLESPACE), a1, c2, y, y + 1);
+    ++y;
+
+    /* cut sliderbutton */
+    p->cut_sb = phin_slider_button_new_with_range(0, 0, 99, 1, 0);
+    phin_slider_button_set_format(PHIN_SLIDER_BUTTON(p->cut_sb), 0,
+                                                    "Cut:", NULL);
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(p->cut_sb),
+                                                    GUI_THRESHOLD);
+    gui_attach(t, p->cut_sb, a1, a2, y, y + 1);
+
+
+    /* cutby sliderbutton */
+    p->cutby_sb = phin_slider_button_new_with_range(0, 0, 99, 1, 0);
+    phin_slider_button_set_format(PHIN_SLIDER_BUTTON(p->cutby_sb), 0,
+                                                    "Cut by:", NULL);
+    phin_slider_button_set_threshold(PHIN_SLIDER_BUTTON(p->cutby_sb),
+                                                    GUI_THRESHOLD);
+    gui_attach(t, p->cutby_sb, b1, b2, y, y + 1);
+    ++y;
+
+    /* portamento control */
+    p->porta_sect = bool_section_new();
+    bool_section_set_bool(  BOOL_SECTION(p->porta_sect),
+                            PATCH_BOOL_PORTAMENTO);
+    gui_attach(t, p->porta_sect, a1, c2, y, y + 1);
+    ++y;
+
+    p->time_sect = float_section_new();
+    float_section_set_float(FLOAT_SECTION(p->time_sect),
+                            PATCH_FLOAT_PORTAMENTO_TIME);
+    gui_attach(t, p->time_sect, a1, c2, y, y + 1);
+    ++y;
+
+    /* mono check button */
+    p->mono_check = gtk_check_button_new_with_label("Monophonic");
+    gui_attach(t, p->mono_check, a1, a2, y, y + 1);
+    ++y;
+
+    /* legato control */
+    p->legato_sect = bool_section_new();
+    bool_section_set_bool(  BOOL_SECTION(p->legato_sect),
+                            PATCH_BOOL_LEGATO);
+    gui_attach(t, p->legato_sect, a1, c2, y, y + 1);
+    ++y;
+
+    /* done! */
+    connect(p);
+}
+
+
+GtkWidget* voice_tab_new(void)
+{
+    return (GtkWidget*) g_object_new(VOICE_TAB_TYPE, NULL);
+}
+
+
+void voice_tab_set_patch(VoiceTab* self, int patch)
+{
+    VoiceTabPrivate* p = VOICE_TAB_GET_PRIVATE(self);
+    int cut, cutby;
+    gboolean porta, mono;
+
+    p->patch = patch;
+
+    if (patch < 0)
+        return;
+
+    cut = patch_get_cut(patch);
+    cutby = patch_get_cut_by(patch);
+    porta = patch_get_portamento(patch);
+    mono = patch_get_monophonic(patch);
+
+    block(p);
+
+    phin_slider_button_set_value(PHIN_SLIDER_BUTTON(p->cut_sb), cut);
+    phin_slider_button_set_value(PHIN_SLIDER_BUTTON(p->cutby_sb), cutby);
+
+    bool_section_set_patch(BOOL_SECTION(p->porta_sect), patch);
+
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->mono_check), mono);
+
+    bool_section_set_patch(BOOL_SECTION(p->legato_sect), patch);
+    float_section_set_patch(FLOAT_SECTION(p->time_sect), patch);
+
+    gtk_widget_set_sensitive(p->legato_sect, mono);
+    gtk_widget_set_sensitive(p->time_sect, porta);
+
+    unblock(p);
+}
diff --git a/src/gui/voicetab.h b/gui/voicetab.h
similarity index 100%
rename from src/gui/voicetab.h
rename to gui/voicetab.h
diff --git a/src/gui/waveform.c b/gui/waveform.c
similarity index 91%
rename from src/gui/waveform.c
rename to gui/waveform.c
index faaaf9c..0c0d8df 100644
--- a/src/gui/waveform.c
+++ b/gui/waveform.c
@@ -100,7 +100,8 @@ static const char* mark_names[] = {
     "Loop start",
     "Loop end",
     "Play end",
-    "Sample end"
+    "Sample end",
+    0
 };
 
 
@@ -136,25 +137,20 @@ G_DEFINE_TYPE(Waveform, waveform, GTK_TYPE_DRAWING_AREA)
 
 static void waveform_dispose(GObject * object);
 
-static void waveform_size_request (GtkWidget * widget,
-                                    GtkRequisition * requisition);
+static void     waveform_size_request(  GtkWidget*, GtkRequisition*);
+static gboolean waveform_expose(        GtkWidget*, GdkEventExpose*);
+static gboolean waveform_button_press(  GtkWidget*, GdkEventButton*);
+static gboolean waveform_configure(     GtkWidget*, GdkEventConfigure*);
+static gboolean waveform_scroll_event(  GtkWidget*, GdkEventScroll*);
 
-static gboolean waveform_expose(GtkWidget * widget,
-                                    GdkEventExpose * event);
-
-static gboolean waveform_button_press(GtkWidget * widget,
-                                    GdkEventButton * event);
-
-static gboolean waveform_configure(GtkWidget * widget,
-                                    GdkEventConfigure * event);
 
 static void waveform_draw(Waveform * wf);
 
 
 static void waveform_class_init (WaveformClass * klass)
 {
-    GObjectClass *object_class = G_OBJECT_CLASS(klass);
-    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+    GObjectClass*   object_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
 
     waveform_parent_class = g_type_class_peek_parent(klass);
 
@@ -163,6 +159,7 @@ static void waveform_class_init (WaveformClass * klass)
     widget_class->configure_event =     waveform_configure;
     widget_class->size_request =        waveform_size_request;
     widget_class->button_press_event =  waveform_button_press;
+    widget_class->scroll_event =        waveform_scroll_event;
 
     signals[PLAY_CHANGED] =
         g_signal_new   ("play-changed",
@@ -336,14 +333,14 @@ waveform_button_press (GtkWidget * widget, GdkEventButton * event)
         {
             if (event->button == 1)
                 mark = WF_MARK_PLAY_START;
-            else if (event->button == 3) 
+            else if (event->button == 3)
                 mark = WF_MARK_PLAY_STOP;
         }
         else
         {
             if (event->button == 1)
                 mark = WF_MARK_LOOP_START;
-            else if (event->button == 3) 
+            else if (event->button == 3)
                 mark = WF_MARK_LOOP_STOP;
         }
     }
@@ -388,6 +385,28 @@ waveform_button_press (GtkWidget * widget, GdkEventButton * event)
 }
 
 
+static gboolean
+waveform_scroll_event(GtkWidget* widget, GdkEventScroll* event)
+{
+    Waveform* wf = WAVEFORM(widget);
+    WaveformPrivate* p = WAVEFORM_GET_PRIVATE(wf);
+    float center;
+
+    if (!p->interactive)
+        return FALSE;
+
+    center = event->x / p->width;
+
+    if (event->direction == GDK_SCROLL_DOWN)
+        waveform_zoom(wf, SCROLL_WHEEL_ZOOM_RATIO, center);
+    else
+        waveform_zoom(wf, 1 / SCROLL_WHEEL_ZOOM_RATIO, center);
+
+    g_signal_emit_by_name(G_OBJECT(wf), "view-changed");
+    return FALSE;
+}
+
+
 inline static void
 cr_rect(cairo_t* cr, double x1, double y1, double x2, double y2)
 {
@@ -397,7 +416,7 @@ cr_rect(cairo_t* cr, double x1, double y1, double x2, double y2)
 
 static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
 {
-    int frames;
+    int frames = -1;
     int start, stop;
     int play_start, play_stop;
     int loop_start, loop_stop;
@@ -407,10 +426,9 @@ static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
     cairo_pattern_t *bg_play;
     cairo_pattern_t *bg_loop;
 
-    frames = patch_get_frames(p->patch);
-
-    if (frames > 0)
+    if (p->patch > -1 && patch_get_sample(p->patch))
     {
+        frames = patch_get_frames(p->patch);
         start = frames * p->range_start;
         stop = frames * p->range_stop;
 
@@ -469,7 +487,8 @@ static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
     {   /* show left dead area if visible */
         int w1 = (play_start < w - 1) ? play_start : w - 1;
         cairo_rectangle (cr, 0, 0, w1, h);
-        cairo_set_source (cr, bg_dead);
+        /*cairo_set_source (cr, bg_dead);*/
+        cairo_set_source_rgb(cr, BG_DEAD_R1, BG_DEAD_G1, BG_DEAD_B1);
         cairo_fill (cr);
 
         if (play_start > w - 1)
@@ -481,7 +500,8 @@ static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
         int x1 = (play_start > 0) ? play_start : 0;
         int x2 = (loop_start < w - 1) ? loop_start : w - 1;
         cr_rect(cr, x1, 0, x2, h);
-        cairo_set_source (cr, bg_play);
+        /*cairo_set_source (cr, bg_play);*/
+        cairo_set_source_rgb(cr, BG_PLAY_R1, BG_PLAY_G1, BG_PLAY_B1);
         cairo_fill (cr);
 
         if (loop_start > w - 1)
@@ -494,7 +514,8 @@ static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
         int x1 = (loop_start > 0) ? loop_start: 0;
         int x2 = (loop_stop < w - 1) ? loop_stop : w - 1;
         cr_rect(cr, x1, 0, x2, h);
-        cairo_set_source(cr, bg_loop);
+        /*cairo_set_source(cr, bg_loop);*/
+        cairo_set_source_rgb(cr, BG_LOOP_R1, BG_LOOP_G1, BG_LOOP_B1);
         cairo_fill(cr);
 
         if (loop_stop > w - 1)
@@ -506,7 +527,8 @@ static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
         int x1 = (loop_stop > 0) ? loop_stop : 0;
         int x2 = (play_stop < w - 1) ? play_stop : w - 1;
         cr_rect(cr, x1, 0, x2, h);
-        cairo_set_source(cr, bg_play);
+        /*cairo_set_source(cr, bg_play);*/
+        cairo_set_source_rgb(cr, BG_PLAY_R1, BG_PLAY_G1, BG_PLAY_B1);
         cairo_fill(cr);
 
         if (play_stop > w - 1)
@@ -517,7 +539,8 @@ static void draw_back(WaveformPrivate* p, int w, int h, cairo_t* cr)
     {   /* right dead area visible */
         int x1 = (play_stop > 0) ? play_stop : 0;
         cr_rect(cr, x1, 0, w, h);
-        cairo_set_source(cr, bg_dead);
+        /*cairo_set_source(cr, bg_dead);*/
+        cairo_set_source_rgb(cr, BG_DEAD_R1, BG_DEAD_G1, BG_DEAD_B1);
         cairo_fill(cr);
     }
 
@@ -535,13 +558,15 @@ static void draw_grid(WaveformPrivate* p, int w, int h, cairo_t* cr)
     int center = h / 2;
     int step = h / GRID_Y;
 
+    /*
     cairo_pattern_t *fg = cairo_pattern_create_linear (0, 0, 0, h);
-
     cairo_pattern_add_color_stop_rgb(fg, 0.0, GRID_R1, GRID_G1, GRID_B1 );
     cairo_pattern_add_color_stop_rgb(fg, 0.5, GRID_R2, GRID_G2, GRID_B2 );
     cairo_pattern_add_color_stop_rgb(fg, 1.0, GRID_R1, GRID_G1, GRID_B1 );
 
     cairo_set_source(cr, fg);
+    */
+    cairo_set_source_rgb(cr, GRID_R1, GRID_G1, GRID_B1);
     cairo_set_line_width(cr, 1.0);
 
     /*  Massive improvement here using Cairo drawing with gradients
@@ -775,11 +800,11 @@ void draw_mark(WaveformPrivate* p, int w, int h, cairo_t* cr)
 
     cairo_text_extents_t extents;
 
-    frames = patch_get_frames (p->patch);
-
-    if (frames <= 0)
+    if (p->patch < 0 || !patch_get_sample(p->patch))
         return;
 
+    frames = patch_get_frames (p->patch);
+
     start = frames * p->range_start;
     stop = frames * p->range_stop;
     play_start = patch_get_mark_frame(p->patch, WF_MARK_PLAY_START);
@@ -902,8 +927,6 @@ static void waveform_draw(Waveform * wf)
         return;
     #endif
 
-    debug("drawing %p\n", p);
-
     cr = gdk_cairo_create(p->pixmap);
 
     cairo_rectangle(cr, 0, 0, p->width, p->height);
@@ -1007,6 +1030,47 @@ int waveform_detect_mark(Waveform* wf)
     return small_mark;
 }
 
+void waveform_zoom(Waveform* wf, gfloat zoom, gfloat center)
+{
+    WaveformPrivate* p = WAVEFORM_GET_PRIVATE(wf);
+
+    float dist;
+    float newstart;
+    float newstop;
+
+    float distl;
+    float distr;
+    float center_cp;
+
+    float newdistl;
+    float newdistr;
+
+    dist = p->range_stop - p->range_start;
+    distl = dist * center;
+    distr = dist * (1 - center);
+
+    center_cp = p->range_start + dist * center;
+
+    newdistl = distl / zoom;
+    newdistr = distr / zoom;
+
+    if (newdistl + newdistr >= 1.0)
+    {
+        waveform_set_range(wf, 0.0, 1.0);
+        return;
+    }
+
+    newstart =  center_cp - newdistl;
+    newstop =   center_cp + newdistr;
+
+    if (newstart < 0.0)
+        newstart = 0.0;
+
+    if (newstop > 1.0)
+        newstop = 1.0;
+
+    waveform_set_range(wf, newstart, newstop);
+}
 
 
 void waveform_set_range (Waveform * wf, float start, float stop)
@@ -1018,7 +1082,7 @@ void waveform_set_range (Waveform * wf, float start, float stop)
                             ? 0.0 
                             : start;
 
-    p->range_stop = (stop < 0.0 || start > 1.0 || start > stop)
+    p->range_stop = (stop < 0.0 || stop > 1.0 || start > stop)
                             ? 1.0
                             : stop;
 
@@ -1078,8 +1142,13 @@ int waveform_get_patch (Waveform * wf)
 void waveform_get_size (Waveform * wf, int *w, int *h)
 {
     WaveformPrivate* p = WAVEFORM_GET_PRIVATE(wf);
-    *w = p->width;
-    *h = p->height;
+
+    if (w)
+        *w = p->width;
+
+    if (h)
+        *h = p->height;
+
     return;
 }
 
diff --git a/src/gui/waveform.h b/gui/waveform.h
similarity index 96%
rename from src/gui/waveform.h
rename to gui/waveform.h
index 1482e10..c2c2a3d 100644
--- a/src/gui/waveform.h
+++ b/gui/waveform.h
@@ -45,7 +45,7 @@
                                     WAVEFORM_TYPE,  WaveformClass))
 
 
-
+#define SCROLL_WHEEL_ZOOM_RATIO 1.5
 
 
 typedef struct _Waveform      Waveform;
@@ -85,6 +85,8 @@ void        waveform_get_size       (Waveform* wf, int* w, int* h);
 void        waveform_get_range      (Waveform* wf, float* start,
                                                    float* stop);
 
+void        waveform_zoom(Waveform* wf, float zoom, float center);
+
 
 void        waveform_goto_mark_prev (Waveform* wf);
 void        waveform_goto_mark_next (Waveform* wf);
diff --git a/install-sh b/install-sh
deleted file mode 120000
index 205f21c..0000000
--- a/install-sh
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/automake-1.11/install-sh
\ No newline at end of file
diff --git a/install_manifest.txt b/install_manifest.txt
new file mode 100644
index 0000000..51e24ec
--- /dev/null
+++ b/install_manifest.txt
@@ -0,0 +1,6 @@
+/usr/local/share/petri-foo/pixmaps/petri-foo.png
+/usr/local/share/petri-foo/pixmaps/petri-foo_small.png
+/usr/local/share/mime/packages/petri-foo.xml
+/usr/local/share/pixmaps/petri-foo.png
+/usr/local/share/applications/petri-foo.desktop
+/usr/local/bin/petri-foo
diff --git a/libpetrifoo/CMakeLists.txt b/libpetrifoo/CMakeLists.txt
new file mode 100644
index 0000000..7f11d83
--- /dev/null
+++ b/libpetrifoo/CMakeLists.txt
@@ -0,0 +1,15 @@
+include_directories ( . )
+
+ADD_SUBDIRECTORY( patch_private )
+
+file (GLOB LIBPETRIFOO_SOURCES *.c)
+
+add_library( petrifoo ${LIBPETRIFOO_SOURCES})
+
+target_link_Libraries(  petrifoo patch_private
+                        ${JACK_LIBRARIES}
+                        ${SNDFILE_LIBRARIES}
+                        ${ALSA_LIBRARIES}
+                        ${SAMPLERATE_LIBRARIES}
+                    )
+
diff --git a/src/adsr.c b/libpetrifoo/adsr.c
similarity index 99%
rename from src/adsr.c
rename to libpetrifoo/adsr.c
index e2cb5b1..09396e5 100644
--- a/src/adsr.c
+++ b/libpetrifoo/adsr.c
@@ -64,7 +64,7 @@ struct _ADSR
 
 void adsr_params_init(ADSRParams* params, float attack, float release)
 {
-    params->env_on =    false;
+    params->active =    false;
     params->delay =     0.0;
     params->attack =    attack;
     params->decay =     0.0;
diff --git a/src/adsr.h b/libpetrifoo/adsr.h
similarity index 99%
rename from src/adsr.h
rename to libpetrifoo/adsr.h
index 041a073..6519a73 100644
--- a/src/adsr.h
+++ b/libpetrifoo/adsr.h
@@ -49,7 +49,7 @@ typedef enum _ADSRState
 /* envelope parameters */
 typedef struct _ADSRParams
 {
-    bool env_on;
+    bool  active;
     float delay;    /* delay length in seconds  */
     float attack;   /* attack length in seconds */
     float decay;    /* decay length in seconds  */
diff --git a/src/driver.c b/libpetrifoo/driver.c
similarity index 77%
rename from src/driver.c
rename to libpetrifoo/driver.c
index 1e9806a..b6cc6c1 100644
--- a/src/driver.c
+++ b/libpetrifoo/driver.c
@@ -30,7 +30,7 @@
 #include "ticks.h"
 #include "patch_util.h"
 
-
+#include <assert.h>
 #include <strings.h> /* strcasecmp */
 
 
@@ -45,7 +45,7 @@ void driver_init(void)
 {
     int i;
 
-    for (i = 0; drivers[i] != NULL; i++)
+    for (i = 0; drivers[i] != NULL; i++, ndrivers++)
         drivers[i]->init();
 
     if ((ndrivers = i) < 0)
@@ -60,8 +60,7 @@ int driver_restart(void)
 
 int driver_start(void)
 {
-    if (ndrivers <= 0)
-        return DRIVER_ERR_OTHER;
+    assert(ndrivers > 0);
 
     if (curdriver >= 0)
         drivers[curdriver]->stop();
@@ -103,32 +102,19 @@ const char* driver_get_name(void)
 
 int driver_set_samplerate(int rate)
 {
-    if (rate <= 0)
-    {
-        debug ("can't accept samplerate %d\n", rate);
-        return DRIVER_ERR_OTHER;
-    }
-
-    /* tell everybody our (potentially new) samplerate */
-      lfo_set_samplerate (rate);
-    patch_set_samplerate (rate);
-    mixer_set_samplerate (rate);
-    ticks_set_samplerate (rate);
+    assert(rate > 0);
+      lfo_set_samplerate(rate);
+    patch_set_samplerate(rate);
+    mixer_set_samplerate(rate);
+    ticks_set_samplerate(rate);
 
     return 0;
 }
 
 int driver_set_buffersize(int nframes)
 {
-    if (nframes <= 0)
-    {
-        debug ("can't accept buffersize %d\n", nframes);
-        return DRIVER_ERR_OTHER;
-    }
-
-     /* tell everybody our (potentially new) buffersize */
-    patch_set_buffersize (nframes);
-
+    assert(nframes > 0);
+    patch_set_buffersize(nframes);
     return 0;
 }
 
diff --git a/src/driver.h b/libpetrifoo/driver.h
similarity index 97%
rename from src/driver.h
rename to libpetrifoo/driver.h
index 285b7e2..eed56c6 100644
--- a/src/driver.h
+++ b/libpetrifoo/driver.h
@@ -36,15 +36,6 @@
 */
 
 
-enum
-{
-     DRIVER_ERR_ID = -1,
-     DRIVER_ERR_OTHER = -2
-};
-
-
-
-
 /* public class definition for drivers */
 typedef struct _Driver
 {
@@ -58,6 +49,7 @@ typedef struct _Driver
 
 } Driver;
 
+
 void        driver_init           (void);
 int         driver_restart        (void);
 int         driver_start          (void);
@@ -67,6 +59,7 @@ int         driver_get_count      (void);
 const char* driver_get_name       (void);
 const char* driver_get_client_name(void);
 
+
 /* this function should only be called by drivers when they start
  * and/or their samplerate changes */
 int driver_set_samplerate (int rate);
diff --git a/src/instance.c b/libpetrifoo/instance.c
similarity index 100%
rename from src/instance.c
rename to libpetrifoo/instance.c
diff --git a/src/instance.h b/libpetrifoo/instance.h
similarity index 100%
rename from src/instance.h
rename to libpetrifoo/instance.h
diff --git a/src/jackdriver.c b/libpetrifoo/jackdriver.c
similarity index 84%
rename from src/jackdriver.c
rename to libpetrifoo/jackdriver.c
index 47c09a3..00456d0 100644
--- a/src/jackdriver.c
+++ b/libpetrifoo/jackdriver.c
@@ -32,18 +32,17 @@
 #include <jack/midiport.h>
 #include <jack/transport.h>
 
-#ifdef HAVE_JACK_SESSION
+#ifdef HAVE_JACK_SESSION_H
 #include <jack/session.h>
-#include "gui/audio-settings.h"
-#endif /* HAVE_JACK_SESSION */
+#endif /* HAVE_JACK_SESSION_H */
 
-#include <gtk/gtk.h>
 #include <pthread.h>
 
 #include "instance.h"
 #include "petri-foo.h"
 #include "driver.h"
 #include "patch.h"
+#include "pf_error.h"
 #include "mixer.h"
 #include "sync.h"
 #include "lfo.h"
@@ -63,8 +62,9 @@ static jack_port_t*    lport;
 static jack_port_t*    rport;
 static jack_port_t*    midiport;
 
-#ifdef HAVE_JACK_SESSION
-static jack_session_event_t *session_event;
+#ifdef HAVE_JACK_SESSION_H
+/*static jack_session_event_t *session_event;*/
+JackSessionCallback         session_cb = 0;
 #endif
 
 static jack_client_t*  client;
@@ -75,9 +75,17 @@ static int             running = 0;
 static pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER;
 static char*           session_uuid = NULL;
 
+static bool             autoconnect = true;
+
 /* working together to stop CTS */
 typedef jack_default_audio_sample_t jack_sample_t;
 
+#ifdef HAVE_JACK_SESSION_H
+void jackdriver_set_session_cb(JackSessionCallback jack_session_cb)
+{
+    session_cb = jack_session_cb;
+}
+#endif
 
 static int process(jack_nframes_t frames, void* arg)
 {
@@ -113,12 +121,12 @@ static int process(jack_nframes_t frames, void* arg)
         if ((last_state == JackTransportStopped)
          || (last_state == JackTransportStarting))
         {
-            debug ("got transport start\n");
+            //debug ("got transport start\n");
             sync_start_jack (new_tempo);
         }
         else if (new_tempo != last_tempo)
         {
-            debug ("got tempo change\n");
+            //debug ("got tempo change\n");
             sync_start_jack (new_tempo);
         }
         last_tempo = new_tempo;
@@ -190,7 +198,7 @@ static int buffer_size_change(jack_nframes_t b, void* arg)
 
     if ((new = malloc(sizeof(float) * b * 2)) == NULL)
     {
-        errmsg("Failed to change buffer size\n");
+        pf_error(PF_ERR_JACK_BUF_SIZE_CHANGE);
         stop();
     }
 
@@ -205,7 +213,7 @@ static int buffer_size_change(jack_nframes_t b, void* arg)
     /* let the rest of the world know the good news */
     driver_set_buffersize (b);
 
-     return 0;
+    return 0;
 }
 
 
@@ -229,11 +237,11 @@ static int start(void)
     const char** ports;
     char* instancename = strdup (get_instance_name());
 
-    debug ("Initializing Jack Driver...\n");
+    debug("JACK initializing driver...\n");
     pthread_mutex_lock (&running_mutex);
     running = 0;
 
-#ifdef HAVE_JACK_SESSION
+#ifdef HAVE_JACK_SESSION_H
     client = jack_client_open(instancename,
                                JackSessionID, NULL, session_uuid);
 #else
@@ -242,7 +250,7 @@ static int start(void)
 
     if (client == 0)
     {
-        errmsg ("Failed to open new jack client: %s\n", instancename);
+        pf_error(PF_ERR_JACK_OPEN_CLIENT);
         pthread_mutex_unlock (&running_mutex);
         return -1;
     }
@@ -251,19 +259,14 @@ static int start(void)
 
     jack_set_process_callback (client, process, 0);
 
-#ifdef HAVE_JACK_SESSION
-    debug("HAVE JACK SESSION\n");
+#ifdef HAVE_JACK_SESSION_H
+
     if (jack_set_session_callback)
     {
-        debug("setting session callback... ");
-
-        if (jack_set_session_callback(client, audio_settings_session_cb, 0))
-        {
-            printf("fail\n");
-        }
-        else
+        if (jack_set_session_callback(client, session_cb, 0))
         {
-            printf("ok\n");
+            pf_error(PF_ERR_JACK_SESSION_CB);
+            return -1;
         }
     }
 #endif
@@ -298,7 +301,7 @@ static int start(void)
 
     if ((buffer = malloc (sizeof (float) * periodsize * 2)) == NULL)
     {
-        errmsg ("Failed to allocate space for buffer\n");
+        pf_error(PF_ERR_JACK_BUF_ALLOC);
         jack_client_close (client);
         pthread_mutex_unlock (&running_mutex);
         return -1;
@@ -308,7 +311,7 @@ static int start(void)
 
     if (jack_activate(client) != 0)
     {
-        errmsg ("Failed to activate client\n");
+        pf_error(PF_ERR_JACK_ACTIVATE);
         jack_client_close(client);
         pthread_mutex_unlock(&running_mutex);
         return -1;
@@ -317,29 +320,28 @@ static int start(void)
     ports = jack_get_ports(client, NULL, NULL,
                             JackPortIsInput | JackPortIsPhysical);
 
-    if (ports[0] != NULL)
+    if (autoconnect)
     {
-        if (jack_connect(client, jack_port_name(lport), ports[0]) != 0)
-            errmsg ("Cannot connect left output port\n");
-
-        if (ports[1] != NULL)
+        if (ports[0] != NULL)
         {
-            if (jack_connect(client, jack_port_name (rport), ports[1]))
-                errmsg ("Cannot connect right output port\n");
+            if (jack_connect(client, jack_port_name(lport), ports[0]) == 0)
+                debug("JACK failed to connect left output port\n");
+
+            if (ports[1] != NULL)
+            {
+                if (jack_connect(client, jack_port_name (rport), ports[1]))
+                    debug("JACK failed to connect right output port\n");
+            }
+            else
+                debug("JACK failed to connect right output port\n");
+
+            free (ports);
         }
         else
-        {
-            errmsg ("Cannot connect right output port\n");
-        }
-
-        free (ports);
-    }
-    else
-    {
-        errmsg ("Cannot connect output ports\n");
+            debug("JACK failed to connect output ports\n");
     }
 
-    debug ("Initialization complete\n");
+    debug("JACK Initialization complete\n");
     running = 1;
     pthread_mutex_unlock (&running_mutex);
 
@@ -353,18 +355,12 @@ static int stop(void)
 
     if (running)
     {
-        debug ("Shutting down...\n");
+        debug("JACK shutting down...\n");
         jack_deactivate (client);
         jack_client_close (client);
 
         if (buffer != NULL)
             free (buffer);
-
-        debug ("Shutdown complete\n");
-    }
-    else
-    {
-        debug ("Not running, so not shutting down\n");
     }
 
     running = 0;
@@ -394,6 +390,13 @@ static void* getid(void)
     return (void*)jack_get_client_name(client);
 }
 
+
+void jackdriver_set_unconnected(void)
+{
+    autoconnect = false;
+}
+
+
 void jackdriver_set_uuid      (char *uuid)
 {
     session_uuid = uuid;
diff --git a/src/jackdriver.h b/libpetrifoo/jackdriver.h
similarity index 84%
rename from src/jackdriver.h
rename to libpetrifoo/jackdriver.h
index 03eb3ca..aef5c54 100644
--- a/src/jackdriver.h
+++ b/libpetrifoo/jackdriver.h
@@ -29,9 +29,16 @@
 
 #include <jack/jack.h>
 
+#ifdef HAVE_JACK_SESSION_H
+#include <jack/session.h>
+void            jackdriver_set_session_cb(JackSessionCallback jacksession_cb);
+#endif
 
+
+void            jackdriver_set_unconnected(void);
 void            jackdriver_set_uuid(char *uuid);
 jack_client_t*  jackdriver_get_client(void);
 
 
+
 #endif /* __JACKDRIVER_H__ */
diff --git a/src/lfo.c b/libpetrifoo/lfo.c
similarity index 98%
rename from src/lfo.c
rename to libpetrifoo/lfo.c
index bda6278..d40e9fc 100644
--- a/src/lfo.c
+++ b/libpetrifoo/lfo.c
@@ -86,7 +86,7 @@ inline static void lfo_phase_inc_from_beats (LFO* lfo, float beats)
 
 void lfo_params_init(LFOParams* lfopar, float freq, LFOShape shape)
 {
-    lfopar->lfo_on =        false;
+    lfopar->active =        false;
     lfopar->shape =         shape;
     lfopar->freq =          freq;
     lfopar->sync_beats =    1.0;
@@ -196,7 +196,7 @@ void lfo_set_tempo(float bpm)
 }
 
 
-void lfo_rigger(LFO* lfo, LFOParams* params)
+void lfo_update_params(LFO* lfo, LFOParams* params)
 {
     lfo->positive = params->positive;
 
@@ -237,7 +237,7 @@ void lfo_rigger(LFO* lfo, LFOParams* params)
 
 void lfo_trigger(LFO* lfo, LFOParams* params)
 {
-    lfo_rigger(lfo, params);
+    lfo_update_params(lfo, params);
     lfo->phase = 0;
     lfo->val = 0;
 }
diff --git a/src/lfo.h b/libpetrifoo/lfo.h
similarity index 96%
rename from src/lfo.h
rename to libpetrifoo/lfo.h
index 8d9ab6a..867bac8 100644
--- a/src/lfo.h
+++ b/libpetrifoo/lfo.h
@@ -58,7 +58,7 @@ typedef enum
 
 typedef struct _LFOParams
 {
-    bool        lfo_on;
+    bool        active;
     LFOShape    shape;
     float       freq;        /* frequency in hz */
     float       sync_beats;
@@ -106,9 +106,7 @@ void    lfo_init(LFO*);
  * after the samplerate/tempo changes in order for those changes to
  * take effect */
 void    lfo_trigger(LFO*, LFOParams*);
-
-/* like lfo_trigger except it don't reset phase */
-void    lfo_rigger(LFO*, LFOParams*);
+void    lfo_update_params(LFO*, LFOParams*);
 
 /* advance an LFO and return its new value */
 float   lfo_tick(LFO*);
diff --git a/src/maths.c b/libpetrifoo/maths.c
similarity index 100%
rename from src/maths.c
rename to libpetrifoo/maths.c
diff --git a/src/maths.h b/libpetrifoo/maths.h
similarity index 100%
rename from src/maths.h
rename to libpetrifoo/maths.h
diff --git a/src/midi.c b/libpetrifoo/midi.c
similarity index 100%
rename from src/midi.c
rename to libpetrifoo/midi.c
diff --git a/src/midi.h b/libpetrifoo/midi.h
similarity index 100%
rename from src/midi.h
rename to libpetrifoo/midi.h
diff --git a/src/midi_control.h b/libpetrifoo/midi_control.h
similarity index 100%
rename from src/midi_control.h
rename to libpetrifoo/midi_control.h
diff --git a/src/mixer.c b/libpetrifoo/mixer.c
similarity index 99%
rename from src/mixer.c
rename to libpetrifoo/mixer.c
index b0600c5..0288a7e 100644
--- a/src/mixer.c
+++ b/libpetrifoo/mixer.c
@@ -198,7 +198,6 @@ void mixer_flush(void)
 /* constructor */
 void mixer_init(void)
 {
-    int p, c;
     debug ("initializing...\n");
     amplitude = DEFAULT_AMPLITUDE;
     pthread_mutex_init (&preview.mutex, NULL);
diff --git a/src/mixer.h b/libpetrifoo/mixer.h
similarity index 100%
rename from src/mixer.h
rename to libpetrifoo/mixer.h
diff --git a/src/mod_src.c b/libpetrifoo/mod_src.c
similarity index 99%
rename from src/mod_src.c
rename to libpetrifoo/mod_src.c
index e09e94d..11d4db0 100644
--- a/src/mod_src.c
+++ b/libpetrifoo/mod_src.c
@@ -262,8 +262,6 @@ int mod_src_id(const char* name, int mask)
 {
     id_name* ids;
 
-    debug("identifying %s\n", name);
-
     if (strcmp(name, "OFF") == 0)
         return MOD_SRC_NONE;
 
diff --git a/src/mod_src.h b/libpetrifoo/mod_src.h
similarity index 100%
copy from src/mod_src.h
copy to libpetrifoo/mod_src.h
diff --git a/src/names.c b/libpetrifoo/names.c
similarity index 99%
rename from src/names.c
rename to libpetrifoo/names.c
index 4f14a04..b7c8ba6 100644
--- a/src/names.c
+++ b/libpetrifoo/names.c
@@ -103,7 +103,6 @@ id_name* id_name_sequence(id_name* start, int first_id, int count,
     int n;
     const int blen = 40;
     char buf[blen];
-    id_name* idnames = start;
 
     for (i = 0; i < count; ++i)
     {
diff --git a/src/names.h b/libpetrifoo/names.h
similarity index 100%
rename from src/names.h
rename to libpetrifoo/names.h
diff --git a/src/patch.c b/libpetrifoo/patch.c
similarity index 88%
rename from src/patch.c
rename to libpetrifoo/patch.c
index 55cf361..b140e39 100644
--- a/src/patch.c
+++ b/libpetrifoo/patch.c
@@ -58,7 +58,7 @@ static float cc[16][CC_ARR_SIZE];
 /*  inline definitions shared by patch and patch_util:
  *                          (see private/patch_data.h)
  */
-INLINE_ISOK_DEF
+INLINE_PATCHOK_DEF
 INLINE_PATCH_LOCK_DEF
 INLINE_PATCH_TRYLOCK_DEF
 INLINE_PATCH_UNLOCK_DEF
@@ -158,7 +158,7 @@ inline static void patch_release_patch(Patch* p, int note, release_t mode)
             /* we don't really release here, that's the job of
              * advance( ); we just tell it *when* to release */
             p->voices[i]->relmode = mode;
-            p->voices[i]->relset = (p->mono && p->legato)
+            p->voices[i]->relset = (p->mono && p->voices[i]->legato)
                                         ? patch_legato_lag
                                         : 0;
         }
@@ -197,25 +197,27 @@ inline static void prepare_pitch(Patch* p, PatchVoice* v, int note)
     /* this applies the tuning factor */
     scale = pow(2, (p->pitch.val * p->pitch_steps) / 12.0);
 
-    if (p->porta && (p->porta_secs > 0.0) && (p->last_note != note))
+    if (v->portamento
+    && (v->porta_secs > 0.0)
+    && (p->last_note != note))
     {
         /* we calculate the pitch here because we can't be certain
          * what the current value of the last voice's pitch is */
         v->pitch =
-            pow(2, (p->last_note - p->note) * p->pitch.key_amt / 12.0);
+            pow(2, (p->last_note - p->root_note) * p->pitch.key_amt / 12.0);
         v->pitch *= scale;
-        v->porta_ticks = ticks_secs_to_ticks (p->porta_secs);
+        v->porta_ticks = ticks_secs_to_ticks(v->porta_secs);
 
         /* calculate the value to be added to pitch each tick by
          * subtracting the target pitch from the initial pitch and
          * dividing by porta_ticks */
         v->pitch_step =
-            ((pow(2, (note - p->note) * p->pitch.key_amt / 12.0)
+            ((pow(2, (note - p->root_note) * p->pitch.key_amt / 12.0)
                              * scale) - v->pitch) / v->porta_ticks;
     }
     else
     {
-        v->pitch = pow(2, (note - p->note) * p->pitch.key_amt / 12.0);
+        v->pitch = pow(2, (note - p->root_note) * p->pitch.key_amt / 12.0);
         v->pitch *= scale;
         v->porta_ticks = 0;
     }
@@ -232,6 +234,26 @@ inline static void prepare_pitch(Patch* p, PatchVoice* v, int note)
 }
 
 
+inline static bool patch_bool_get(PatchBool* pb, Patch* p)
+{
+    const float* mod = patch_mod_id_to_pointer(pb->mod_id, p, NULL);
+    return (pb->active && pb->mod_id) ? (*mod > pb->thresh) : pb->active;
+}
+
+inline static float patch_float_get(PatchFloat* pf, Patch* p)
+{
+    float value = pf->val;
+
+    if (pf->mod_id)
+    {
+        const float* mod = patch_mod_id_to_pointer(pf->mod_id, p, NULL);
+        value += *mod * pf->mod_amt;
+    }
+
+    return value;
+}
+
+
 /**************************************************************************
  *********************     PATCH TRIGGER PATCH      ***********************
  **************************************************************************/
@@ -244,6 +266,7 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
     PatchVoice* v;
     int index;          /* the index we ended up settling on */
     float key_track;
+    bool legato;
 
     if (p->sample->sp == NULL)
         return;
@@ -254,7 +277,9 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
         key_track = (float)(note - p->lower_note)
                                 / (p->upper_note - p->lower_note);
 
-    if (p->mono && p->legato)
+    legato = patch_bool_get(&p->legato, p);
+
+    if (p->mono && legato)
     {
         /*  half of the previous logic operating here was ignored.
          *  removing it left only logic which could be simplified
@@ -268,6 +293,7 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
             v->vel = vel;
         else
         {
+            /* don't trigger voice, do legato instead: */
             v->ticks =      ticks;
             v->note =       note;
             v->vel =        vel;
@@ -278,9 +304,9 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
             v->xfade =      false;
             v->loop =       p->play_mode & PATCH_PLAY_LOOP;
             v->key_track =  key_track;
-
+            v->portamento = patch_bool_get(&p->porta, p);
+            v->porta_secs = patch_float_get(&p->porta_secs, p);
             prepare_pitch(p, v, note);
-
             return;
         }
     }
@@ -323,8 +349,11 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
     v->loop =       p->play_mode & PATCH_PLAY_LOOP;
     v->note =       note;
     v->key_track =  key_track;
+    v->legato =     legato;
+    v->portamento = patch_bool_get(&p->porta, p);
+    v->porta_secs = patch_float_get(&p->porta_secs, p);
 
-    if (!(p->mono && p->legato))
+    if (!(p->mono && v->legato))
         v->vel = vel;
 
     if (!p->mono)
@@ -332,21 +361,21 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
 
     for (i = 0; i < MAX_MOD_SLOTS; ++i)
     {
-        v->vol_mod[i] = patch_mod_id_to_pointer(p->vol.mod_id[i], p, v);
+        v->amp_mod[i] = patch_mod_id_to_pointer(p->amp.mod_id[i], p, v);
         v->pan_mod[i] = patch_mod_id_to_pointer(p->pan.mod_id[i], p, v);
         v->ffreq_mod[i] = patch_mod_id_to_pointer(p->ffreq.mod_id[i], p, v);
         v->freso_mod[i] = patch_mod_id_to_pointer(p->freso.mod_id[i], p, v);
         v->pitch_mod[i] = patch_mod_id_to_pointer(p->pitch.mod_id[i], p, v);
     }
 
-    if (!(p->mono && p->legato && v->active))
+    if (!(p->mono && v->legato && v->active))
         playstate_init_fade_in(p, v);
 
     prepare_pitch(p, v, note);
 
     for (i = 0; i < VOICE_MAX_ENVS; i++)
     {
-        if (p->env_params[i].env_on)
+        if (p->env_params[i].active)
         {
             adsr_set_params(v->env[i], &p->env_params[i]);
             adsr_trigger(v->env[i], key_track, vel);
@@ -355,7 +384,7 @@ patch_trigger_patch (Patch* p, int note, float vel, Tick ticks)
 
     for (i = 0; i < VOICE_MAX_LFOS; i++)
     {
-        if (p->vlfo_params[i].lfo_on)
+        if (p->vlfo_params[i].active)
         {
             float const* src;
 
@@ -473,7 +502,10 @@ pan (Patch * p, PatchVoice * v, int index, float *l, float *r)
             pan += *v->pan_mod[i] * p->pan.mod_amt[i];
 
     /* scale for velocity tracking */
-    pan = lerp(pan, pan * v->vel, p->pan.vel_amt);
+    if (p->pan.vel_amt < 0)
+        pan = lerp(pan, pan * (1.0 - v->vel), p->pan.vel_amt * -1);
+    else
+        pan = lerp(pan, pan * v->vel, p->pan.vel_amt);
 
     /* scale for key tracking */
     if (p->pan.key_amt < 0)
@@ -514,7 +546,11 @@ filter (Patch* p, PatchVoice* v, int index,  float* l, float* r)
             ffreq += *v->ffreq_mod[i] * p->ffreq.mod_amt[i];
 
     /* scale to velocity */
-    ffreq = lerp (ffreq, ffreq * v->vel, p->ffreq.vel_amt);
+    if (p->ffreq.vel_amt < 0)
+        ffreq = lerp(ffreq, ffreq * (1.0 - v->vel), p->ffreq.vel_amt * -1);
+    else
+        ffreq = lerp(ffreq, ffreq * v->vel, p->ffreq.vel_amt);
+
 
     /* scale for key tracking */
     if (p->ffreq.key_amt < 0)
@@ -538,7 +574,10 @@ filter (Patch* p, PatchVoice* v, int index,  float* l, float* r)
             freso += *v->freso_mod[i] * p->freso.mod_amt[i];
 
     /* scale to velocity */
-    freso = lerp(freso, freso * v->vel, p->freso.vel_amt);
+    if (p->freso.vel_amt < 0)
+        freso = lerp(freso, freso * (1.0 - v->vel), p->freso.vel_amt * -1);
+    else
+        freso = lerp(freso, freso * v->vel, p->freso.vel_amt);
 
     /* scale for key tracking */
     if (p->freso.key_amt < 0)
@@ -554,16 +593,16 @@ filter (Patch* p, PatchVoice* v, int index,  float* l, float* r)
     else if (freso < 0.0)
         freso = 0.0;
 
-    /* logify */
-    logreso = log_amplitude(freso);
+    /* logify - seems better without this:
+    logreso = log_amplitude(freso); */
 
     /* left */
-    v->fbl = logreso * v->fbl + ffreq * (*l - v->fll);
+    v->fbl = freso * v->fbl + ffreq * (*l - v->fll);
     v->fll += ffreq * v->fbl;
     *l = v->fll;
 
     /* right */
-    v->fbr = logreso * v->fbr + ffreq * (*r - v->flr);
+    v->fbr = freso * v->fbr + ffreq * (*r - v->flr);
     v->flr += ffreq * v->fbr;
     *r = v->flr;
 }
@@ -574,56 +613,60 @@ inline static int
 gain (Patch* p, PatchVoice* v, int index, float* l, float* r)
 {
     int i;
-    float vol = 0.0;
-    float logvol = 0.0;
+    float amp = 0.0;
+    float logamp = 0.0;
 
     /* first, we use our set value as a base */
-    vol = p->vol.val;
+    amp = p->amp.val;
 
     for (i = 0; i < EG_MOD_SLOT; ++i)
-        if (v->vol_mod[i] != NULL)
-            vol += *v->vol_mod[i] * p->vol.mod_amt[i];
+        if (v->amp_mod[i] != NULL)
+            amp += *v->amp_mod[i] * p->amp.mod_amt[i];
 
     /* direct modulation source (ie no amount) */
-    if (v->vol_mod[EG_MOD_SLOT])
-        vol *= *v->vol_mod[EG_MOD_SLOT];
+    if (v->amp_mod[EG_MOD_SLOT])
+        amp *= *v->amp_mod[EG_MOD_SLOT];
 
     /* scale for key tracking */
-    if (p->vol.key_amt < 0)
-        vol = lerp(vol, vol * (1.0 - v->key_track), p->vol.key_amt * -1);
+    if (p->amp.key_amt < 0)
+        amp = lerp(amp, amp * (1.0 - v->key_track), p->amp.key_amt * -1);
     else
-        vol = lerp(vol, vol * v->key_track, p->vol.key_amt);
+        amp = lerp(amp, amp * v->key_track, p->amp.key_amt);
 
     /* velocity should be the last parameter considered because it
      * has the most "importance" */
-    vol = lerp(vol, vol * v->vel, p->vol.vel_amt);
+    if (p->amp.vel_amt < 0)
+        amp = lerp(amp, amp * (1.0 - v->vel), p->amp.vel_amt * -1);
+    else
+        amp = lerp(amp, amp * v->vel, p->amp.vel_amt);
+
 
     /* apply fade in/out */
-    vol *= v->fade_declick;
+    amp *= v->fade_declick;
 
     /* clip */
-    if (vol > 1.0)
-        vol = 1.0;
-    else if (vol < 0.0)
-        vol = 0.0;
+    if (amp > 1.0)
+        amp = 1.0;
+    else if (amp < 0.0)
+        amp = 0.0;
 
     /* as a last step, make logarithmic */
-/*   logvol = log_amplitude(vol);
+/*   logamp = log_amplitude(amp);
 */
 
     /* adjust amplitude */
 
-    *l *= vol;
-    *r *= vol;
+    *l *= amp;
+    *r *= amp;
 
 /*
-    *l *= logvol;
-    *r *= logvol;
+    *l *= logamp;
+    *r *= logamp;
 */
     /* check to see if we've finished a release */
     if (v->released && (v->fade_declick == 0.0f //< ALMOST_ZERO
-                    || (v->vol_mod[EG_MOD_SLOT] 
-                    && *v->vol_mod[EG_MOD_SLOT] < ALMOST_ZERO)))
+                    || (v->amp_mod[EG_MOD_SLOT] 
+                    && *v->amp_mod[EG_MOD_SLOT] < ALMOST_ZERO)))
     {
         return -1;
     }
@@ -681,7 +724,7 @@ inline static int advance (Patch* p, PatchVoice* v, int index)
         /* whether we need to recalculate our pos/step vars */
 
     /* portamento */
-    if (p->porta && v->porta_ticks)
+    if (v->portamento && v->porta_ticks)
     {
         recalc = true;
         v->pitch += v->pitch_step;
@@ -723,6 +766,11 @@ inline static int advance (Patch* p, PatchVoice* v, int index)
         recalc = true;
         pitch = lerp (pitch, pitch * v->vel, p->pitch.vel_amt);
     }
+    else if (p->pitch.vel_amt < -ALMOST_ZERO)
+    {
+        recalc = true;
+        pitch = lerp (pitch, pitch * (1.0 - v->vel), -p->pitch.vel_amt);
+    }
 
     if (recalc)
     {
@@ -855,7 +903,7 @@ inline static int advance (Patch* p, PatchVoice* v, int index)
 
                 if (!(p->play_mode & PATCH_PLAY_SINGLESHOT))
                 {
-                    if (!v->vol_mod[EG_MOD_SLOT]) /* direct mod source */
+                    if (!v->amp_mod[EG_MOD_SLOT]) /* direct mod source */
                     {
                         playstate_init_fade_out(p, v);
                     }
@@ -915,7 +963,7 @@ inline static void patch_render_patch (Patch* p, float* buf, int nframes)
     {
         for (j = 0; j < PATCH_MAX_LFOS; ++j)
         {
-            if (p->glfo_params[j].lfo_on)
+            if (p->glfo_params[j].active)
                 p->glfo_table[j][i] = lfo_tick(p->glfo[j]);
         }
     }
@@ -944,15 +992,15 @@ inline static void patch_render_patch (Patch* p, float* buf, int nframes)
                 the correct value for the frame.
             */
             for (k = 0; k < PATCH_MAX_LFOS; ++k)
-                if (p->glfo_params[k].lfo_on)
+                if (p->glfo_params[k].active)
                     lfo_set_output(p->glfo[k], p->glfo_table[k][j]);
 
             for (k = 0; k < VOICE_MAX_ENVS; ++k)
-                if (p->env_params[k].env_on)
+                if (p->env_params[k].active)
                     adsr_tick(v->env[k]);
 
             for (k = 0; k < VOICE_MAX_LFOS; ++k)
-                if (p->vlfo_params[k].lfo_on)
+                if (p->vlfo_params[k].active)
                     lfo_tick(v->lfo[k]);
 
             /* process samples */
@@ -1056,7 +1104,7 @@ void patch_render (float *buf, int nframes)
 /* triggers all patches matching criteria */
 void patch_trigger (int chan, int note, float vel, Tick ticks)
 {
-    static int idp[PATCH_COUNT];	/* holds all patches to be activated */
+    static int idp[PATCH_COUNT]; /* holds all patches to be activated */
     int i, j;
 
     /* We gather up all of the patches that need to be activated here
@@ -1066,14 +1114,16 @@ void patch_trigger (int chan, int note, float vel, Tick ticks)
      * the same note and have the same cut/cut_by values will end up
      * stepping over each other before they both are heard.
      */
-
+    int int_vel = (int)(vel * 127.0);
     for (i = j = 0; i < PATCH_COUNT; i++)
     {
         if (patches[i]
          && patches[i]->active
          && patches[i]->channel == chan
          && (note >= patches[i]->lower_note
-         &&  note <= patches[i]->upper_note))
+          && note <= patches[i]->upper_note)
+         && (int_vel >= patches[i]->lower_vel
+          && int_vel <= patches[i]->upper_vel))
         {
             idp[j++] = i;
         }
@@ -1085,7 +1135,9 @@ void patch_trigger (int chan, int note, float vel, Tick ticks)
 
     /* do triggers */
     for (i = 0; i < j; i++)
+    {    
         patch_trigger_patch(patches[idp[i]], note, vel, ticks);
+    }
 }
 
 
@@ -1107,47 +1159,6 @@ void patch_trigger_with_id (int id, int note, float vel, Tick ticks)
 }
 
 
-static void patch_control_patch(Patch* p, int param, float value)
-{
-/*
-    switch( param )
-    {
-    case CONTROL_PARAM_MODWHEEL:
-        break;
-    case CONTROL_PARAM_AMPLITUDE:
-        p->vol.val = value;
-        break;
-    case CONTROL_PARAM_PANNING:
-        p->pan.val = value;
-        break;
-    case CONTROL_PARAM_CUTOFF:
-        p->ffreq.val = value;
-        break;
-    case CONTROL_PARAM_RESONANCE:
-        p->freso.val = value;
-        break;
-    case CONTROL_PARAM_PITCH:
-        p->pitch_bend = pow(2, value);
-        break;
-    case CONTROL_PARAM_PORTAMENTO:
-        p->porta = (value < 0.5) ? 0 : 1;
-        break;
-    case CONTROL_PARAM_PORTAMENTO_TIME:
-        p->porta_secs = value;
-        break;
-    case CONTROL_PARAM_CUTOFF_MOD1_AMT:
-        p->ffreq.mod1_amt = value;
-        break;
-    case CONTROL_PARAM_RESO_MOD1_AMT:
-        p->freso.mod1_amt = value;
-        break;
-    default:
-        break;
-    }
-*/
-}
-
-
 void patch_control_init(void)
 {
     int c, p;
@@ -1168,20 +1179,9 @@ void patch_control_init(void)
 
 void patch_control(int chan, int param, float value)
 {
-    int i;
-
+    /* FIXME: this could probably be put back into mixer and
+                a function call could be saved ?
+     */
     cc[chan][1 + param] = value;
-
-    for (i = 0; i < PATCH_COUNT; i++)
-    {
-        if (patches[i]
-         && patches[i]->active
-         && patches[i]->channel == chan)
-        {
-            patch_control_patch(patches[i], param, value);
-        }
-    }
-
-    return;
 }
 
diff --git a/src/patch.h b/libpetrifoo/patch.h
similarity index 80%
rename from src/patch.h
rename to libpetrifoo/patch.h
index fa965f6..6b9c4c4 100644
--- a/src/patch.h
+++ b/libpetrifoo/patch.h
@@ -111,52 +111,33 @@ typedef enum _MOD_SRC_ID_BITMASK
 } mod_src_id_bitmask;
 
 
-/* error codes */
-enum
-{
-/*   PATCH_PARAM_INVALID =          -1,  */
-     PATCH_ID_INVALID =             -2,
-     PATCH_ALLOC_FAIL =             -3,
-     PATCH_NOTE_INVALID =           -4,
-     PATCH_PAN_INVALID =            -5,
-     PATCH_CHANNEL_INVALID =        -6,
-     PATCH_VOL_INVALID =            -7,
-     PATCH_PLAY_MODE_INVALID =      -9,
-     PATCH_LIMIT =                  -10,
-     PATCH_SAMPLE_INDEX_INVALID =   -11,
-     PATCH_ENV_ID_INVALID =         -12,
-     PATCH_LFO_ID_INVALID =         -13,
-     PATCH_MOD_SRC_INVALID =        -14,
-     PATCH_MOD_AMOUNT_INVALID =     -15,
-     PATCH_MOD_SLOT_INVALID
-};
-
-
 /* These are the bitfield constants for the different ways a patch can
    be played.  I've used comments to indicate mutual exclusion among
    groups. */
+
+/* note:    i dislike this setup, not the use of bitfields per se...
+ */
+
 enum
 {
      /* direction */
-     PATCH_PLAY_FORWARD =       1 << 0,
-     PATCH_PLAY_REVERSE =       1 << 1,
-     /************/
+     PATCH_PLAY_REVERSE =       0x0001, /* if not reverse then what!?!? */
 
      /* duration */
-     PATCH_PLAY_SINGLESHOT =    1 << 2,
-     PATCH_PLAY_TRIM =          1 << 3,
-     PATCH_PLAY_LOOP =          1 << 4,
-     /***********/
+     PATCH_PLAY_SINGLESHOT =    0x0002,
+     PATCH_PLAY_TRIM =          0x0004,
+     PATCH_PLAY_LOOP =          0x0008,
 
      /* ping pong mode can be set independently of all the other
-      * params, but it should only be tested for if PATCH_PLAY_LOOP is set */
-     PATCH_PLAY_PINGPONG =      1 << 5,
+      * params, but it should only be tested for if PATCH_PLAY_LOOP is set
+      */
+     PATCH_PLAY_PINGPONG =      0x0010,
 
     /*  patch play to end should only be tested for if PATCH_PLAY_LOOP is
         set. if active, after note_off, playback continues past loop end
         toward sample end
      */
-     PATCH_PLAY_TO_END =        1 << 6,
+     PATCH_PLAY_TO_END =        0x0020,
 };
 
 
@@ -178,8 +159,8 @@ typedef uint8_t PatchPlayMode;
 /* code names for modulatable parameters */
 typedef enum
 {
-    PATCH_PARAM_INVALID =       -1,
-    PATCH_PARAM_AMPLITUDE =    0,
+    PATCH_PARAM_INVALID =   -1,
+    PATCH_PARAM_AMPLITUDE = 0,
     PATCH_PARAM_PANNING,
     PATCH_PARAM_PITCH,
     PATCH_PARAM_CUTOFF,
@@ -188,6 +169,25 @@ typedef enum
 } PatchParamType;
 
 
+typedef enum
+{
+    PATCH_BOOL_INVALID =        -1,
+    PATCH_BOOL_PORTAMENTO =     0,
+    PATCH_BOOL_MONO,
+    PATCH_BOOL_LEGATO,
+
+} PatchBoolType;
+
+
+typedef enum
+{
+    PATCH_FLOAT_INVALID =           -1,
+    PATCH_FLOAT_PORTAMENTO_TIME =   0
+
+} PatchFloatType;
+
+
+
 void patch_control_init    (void);
 
 /* playback and rendering functions  */
diff --git a/src/ticks.c b/libpetrifoo/patch_event.c
similarity index 65%
copy from src/ticks.c
copy to libpetrifoo/patch_event.c
index fdae58e..b6c27dc 100644
--- a/src/ticks.c
+++ b/libpetrifoo/patch_event.c
@@ -1,7 +1,5 @@
 /*  Petri-Foo is a fork of the Specimen audio sampler.
 
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
     Copyright 2011 James W. Morris
 
     This file is part of Petri-Foo.
@@ -17,27 +15,40 @@
 
     You should have received a copy of the GNU General Public License
     along with Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
 */
 
 
-#include <sys/time.h>
-#include "petri-foo.h"
-#include "driver.h"
-#include "ticks.h"
+#include "patch_event.h"
 
+#include "patch_private/patch_event_data.h"
 
-static int samplerate = -1;
 
 
-Tick ticks_secs_to_ticks (float secs)
+int patch_event_set(int patch_id, EvType evtype, ...)
 {
-     return samplerate * secs;
 }
 
 
-void ticks_set_samplerate (int rate)
+BaseEvent* patch_event_get(int patch_id, EvType evtype, ...)
 {
-     samplerate = rate;
 }
+
+
+
+/* assertions on these to make sure correct type used */
+bool base_event_get_bool(BaseEvent* ev)
+{
+}
+
+
+float base_event_get_float(BaseEvent* ev)
+{
+}
+
+
+int base_event_get_int(BaseEvent* ev)
+{
+}
+
+
+
diff --git a/libpetrifoo/patch_event.h b/libpetrifoo/patch_event.h
new file mode 100644
index 0000000..3a3b04e
--- /dev/null
+++ b/libpetrifoo/patch_event.h
@@ -0,0 +1,115 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef PATCH_EVENT_H
+#define PATCH_EVENT_H
+
+
+#include <stdbool.h>
+
+
+typedef enum _EventType
+{
+    EV_TYPE_EG,     /* patch_id, eg_id,  var_id */
+    EV_TYPE_LFO,    /* patch_id, lfo_id, var_id */
+    EV_TYPE_LFO_AM, /* patch_id, lfo_id, AM slot_id, var_id */
+    EV_TYPE_LFO_FM, /* patch_id, lfo_id, FM slot_id, var_id */
+    EV_TYPE_MAIN,   /* patch_id, var_id */
+    EV_TYPE_MARK,   /* patch_id, mark_id */
+
+} EvType;
+
+
+
+enum
+{
+    EV_INVALID_EVENT = -1,
+
+    /* bool settings */
+    EV__ACTIVE,
+    EV__LEGATO,
+    EV__MONO,
+    EV__ON,
+    EV__PORTAMENTO,
+    EV__POSITIVE,
+    EV__SYNC,
+
+    /* char settings */
+    EV__NAME,
+
+    /* float settings */
+    EV__AMPLITUDE,
+    EV__ASSIGN,
+    EV__ATTACK,
+    EV__CUTOFF,
+    EV__DECAY,
+    EV__DELAY,
+    EV__FREQ,
+    EV__HOLD,
+    EV__KEY_AMT,
+    EV__MOD_AMT,
+    EV__PAN,
+    EV__PORTAMENTO_TIME,
+    EV__PITCH,
+    EV__RELEASE,
+    EV__RESONANCE,
+    EV__SUSTAIN,
+    EV__SYNC_BEATS,
+    EV__THRESH,
+    EV__VALUE,
+    EV__VEL_AMT,
+
+    /* int settings */
+    EV__CHANNEL,
+    EV__CUT,
+    EV__CUT_BY,
+    EV__FADE_SAMPLES,
+    EV__LFO_SHAPE,
+    EV__LOOP_START,
+    EV__LOOP_STOP,
+    EV__LOWER_NOTE,
+    EV__LOWER_VEL,
+    EV__MOD_SRC,
+    EV__PITCH_STEPS,
+    EV__PLAY_MODE,
+    EV__PLAY_START,
+    EV__PLAY_STOP,
+    EV__ROOT_NOTE,
+    EV__UPPER_NOTE,
+    EV__UPPER_VEL,
+    EV__XFADE_SAMPLES,
+
+    EV_LAST_EVENT_XXX
+};
+
+
+
+typedef struct _BaseEvent BaseEvent;
+
+
+int         patch_event_set(    int patch_id, EvType, ...);
+BaseEvent*  patch_event_get(    int patch_id, EvType, ...);
+
+/* assertions on these to make sure correct type used */
+bool    base_event_get_bool(    BaseEvent*);
+float   base_event_get_float(   BaseEvent*);
+int     base_event_get_int(     BaseEvent*);
+
+#endif
diff --git a/libpetrifoo/patch_private/CMakeLists.txt b/libpetrifoo/patch_private/CMakeLists.txt
new file mode 100644
index 0000000..271952b
--- /dev/null
+++ b/libpetrifoo/patch_private/CMakeLists.txt
@@ -0,0 +1,2 @@
+file (GLOB LIBPATCHPRIVATE_SOURCES *.c)
+add_library( patch_private ${LIBPATCHPRIVATE_SOURCES})
diff --git a/src/patch_private/patch_data.c b/libpetrifoo/patch_private/patch_data.c
similarity index 85%
rename from src/patch_private/patch_data.c
rename to libpetrifoo/patch_private/patch_data.c
index 345757a..6b2e883 100644
--- a/src/patch_private/patch_data.c
+++ b/libpetrifoo/patch_private/patch_data.c
@@ -50,16 +50,22 @@ Patch* patch_new(void)
 
     p->name[0] = '\0';
 
-    p->active =         true;
+    p->active =         false;
     p->sample =         sample_new();
     p->display_index =  -1;
+
+    p->name[0] = '\0';
+
     p->channel =        0;
-    p->note =           60;
+    p->root_note =      60;
     p->lower_note =     60;
     p->upper_note =     60;
+    p->lower_vel =      0;
+    p->upper_vel =      127;
 
     p->cut =            0;
     p->cut_by =         0;
+
     p->play_start =     0;
     p->play_stop =      0;
     p->loop_start =     0;
@@ -75,19 +81,30 @@ Patch* patch_new(void)
 
     p->fade_samples =   0;
     p->xfade_samples =  0;
-    p->porta =          false;
-    p->porta_secs =     0.05;
+
+    p->porta.active =   true;   /* but only if PORTAMENTO   */
+    p->porta.thresh =   0.5;    /* controller says so...    */
+    p->porta.mod_id =   MOD_SRC_MIDI_CC + CC_PORTAMENTO;
+
+    p->porta_secs.val =     0.05;
+    p->porta_secs.mod_amt = 1.0;
+    p->porta_secs.mod_id =  MOD_SRC_MIDI_CC + CC_PORTAMENTO_TIME;
+
     p->pitch_steps =    2;
     p->pitch_bend =     0;
-    p->mono =           false;
-    p->legato =         false;
 
-    p->play_mode =      PATCH_PLAY_SINGLESHOT | PATCH_PLAY_FORWARD;
+    p->mono = false;
+
+    p->legato.active =  true;   /* but only if mono is on, *AND*    */
+    p->legato.thresh =  0.5;    /* LEGATO controller says so...     */
+    p->legato.mod_id =  MOD_SRC_MIDI_CC + CC_LEGATO;
+
+    p->play_mode =      PATCH_PLAY_SINGLESHOT;
 
     for (i = 0; i < MAX_MOD_SLOTS; ++i)
     {
-        p->vol.mod_id[i] = MOD_SRC_NONE;
-        p->vol.mod_amt[i] = 0.0;
+        p->amp.mod_id[i] = MOD_SRC_NONE;
+        p->amp.mod_amt[i] = 0.0;
 
         p->pan.mod_id[i] = MOD_SRC_NONE;
         p->pan.mod_amt[i] = 0.0;
@@ -105,9 +122,9 @@ Patch* patch_new(void)
         p->mod_pitch_max[i] = 1.0;
     }
 
-    p->vol.val =        DEFAULT_AMPLITUDE;
-    p->vol.vel_amt =    1.0;
-    p->vol.key_amt =    0.0;
+    p->amp.val =        DEFAULT_AMPLITUDE;
+    p->amp.vel_amt =    1.0;
+    p->amp.key_amt =    0.0;
 
     p->pan.val =        0.0;
     p->pan.vel_amt =    0;
@@ -222,15 +239,15 @@ float const* patch_mod_id_to_pointer(int id, Patch* p, PatchVoice* v)
 {
     switch(id)
     {
-    case MOD_SRC_NONE:      return 0;
+    case MOD_SRC_NONE:      return NULL;
     case MOD_SRC_ONE:       return &one;
-    case MOD_SRC_VELOCITY:  return &v->vel;
-    case MOD_SRC_KEY:       return &v->key_track;
+    case MOD_SRC_VELOCITY:  return (v) ? &v->vel :          NULL;
+    case MOD_SRC_KEY:       return (v) ? &v->key_track :    NULL;
     case MOD_SRC_PITCH_WHEEL:
         return &((*cc_arr)[p->channel][0]);
     }
 
-    if (id & MOD_SRC_EG)
+    if (id & MOD_SRC_EG && v)
     {
         id &= ~MOD_SRC_EG;
 
@@ -238,7 +255,7 @@ float const* patch_mod_id_to_pointer(int id, Patch* p, PatchVoice* v)
             return adsr_output(v->env[id]);
     }
 
-    if (id & MOD_SRC_VLFO)
+    if (id & MOD_SRC_VLFO && v)
     {
         id &= ~MOD_SRC_VLFO;
 
@@ -270,14 +287,14 @@ void patch_copy(Patch* dest, Patch* src)
 {
     int i;
 
-    dest->active = true;
+    dest->active = false;
 
     sample_deep_copy(dest->sample, src->sample);
 
     strcpy(dest->name, src->name);
 
     dest->channel =         src->channel;
-    dest->note =            src->note;
+    dest->root_note =       src->root_note;
     dest->lower_note =      src->lower_note;
     dest->upper_note =      src->upper_note;
     dest->cut =             src->cut;
@@ -298,7 +315,7 @@ void patch_copy(Patch* dest, Patch* src)
     dest->legato =          src->legato;
     dest->play_mode =       src->play_mode;
 
-    dest->vol =             src->vol;
+    dest->amp =             src->amp;
     dest->pan =             src->pan;
     dest->ffreq =           src->ffreq;
     dest->freso =           src->freso;
diff --git a/src/patch_private/patch_data.h b/libpetrifoo/patch_private/patch_data.h
similarity index 55%
rename from src/patch_private/patch_data.h
rename to libpetrifoo/patch_private/patch_data.h
index 9ea5e44..cbb2bf8 100644
--- a/src/patch_private/patch_data.h
+++ b/libpetrifoo/patch_private/patch_data.h
@@ -28,15 +28,17 @@
 
 #include "midi_control.h"
 #include "patch.h"
-
 #include "patch_voice.h"
-
 #include "sample.h"
 
 
+/*  PatchParam
+        a structure used for sound parameters which can be modulated
+        continually as the voice plays.
+ */
 typedef struct _PatchParam
 {
-    float   val;        /* value of this parameter */
+    float   val;
 
     /* modulation sources */
     int     mod_id[MAX_MOD_SLOTS];
@@ -49,26 +51,58 @@ typedef struct _PatchParam
 } PatchParam;
 
 
+typedef struct _PatchFloat
+{
+    float   val;
+    int     mod_id;
+    float   mod_amt;
+
+} PatchFloat;
+
+
+/*  PatchBool
+        a structure used for storing boolean settings which can be turned
+        on and off by a modulation source.
+
+        *usually* these settings will not be continually modified by the
+        modulation source; their value will usually only be taken each
+        time a voice is triggered.
+
+        modulation values below the threshold turn the feature off, while
+        those above turn it on.
+ */
+typedef struct _PatchBool
+{
+    bool    active;
+    int     mod_id;
+    float   thresh;
+
+} PatchBool;
+
+
 /* type for array of instruments (called patches) */
 struct _Patch
 {
-    bool     active;        /* whether patch is in use or not */
-    Sample*  sample;        /* sample data */
-    int      display_index; /* order in which this Patch to be displayed */
-
-    char     name[PATCH_MAX_NAME];
-
-    int      channel;       /* midi channel to listen on */
-    int      note;          /* midi note to listen on */
-    int      lower_note;    /* lowest note in range */
-    int      upper_note;    /* highest note in range */
-    int      cut;           /* cut signal this patch emits */
-    int      cut_by;        /* what cut signals stop this patch */
-    int      play_start;    /* the first frame to play */
-    int      play_stop;     /* the last frame to play */
-    int      loop_start;    /* the first frame to loop at */
-    int      loop_stop;     /* the last frame to loop at */
-
+    bool    active;         /* whether patch is in use or not */
+    Sample* sample;         /* sample data */
+    int     display_index;  /* order in which this Patch to be displayed */
+
+    char    name[PATCH_MAX_NAME];
+
+    int     channel;        /* midi channel to listen on */
+    int     root_note;      /* midi note to listen on */
+    int     lower_note;     /* lowest note in range */
+    int     upper_note;     /* highest note in range */
+    int     lower_vel;      /* lower velocity trigger */
+    int     upper_vel;      /* upper velocity trigger */
+
+    int     cut;            /* cut signal this patch emits */
+    int     cut_by;         /* what cut signals stop this patch */
+
+    int     play_start;     /* the first frame to play */
+    int     play_stop;      /* the last frame to play */
+    int     loop_start;     /* the first frame to loop at */
+    int     loop_stop;      /* the last frame to loop at */
     int     sample_stop;    /* very last frame in sample */
 
     int*    marks[WF_MARK_STOP + 1];
@@ -76,19 +110,21 @@ struct _Patch
     int     fade_samples;
     int     xfade_samples;
 
-    bool    porta;          /* whether portamento is being used or not */
-    float   porta_secs;     /* length of portamento slides in seconds */
-    int     pitch_steps;    /* range of pitch.val in halfsteps */
-    float   pitch_bend;     /* pitch bending factor */
-    bool    mono;           /* whether patch is monophonic or not */
-    bool    legato;         /* whether patch is played legato or not */
+    PatchBool   porta;
+    PatchFloat  porta_secs;
+
+    int         pitch_steps;    /* range of pitch.val in halfsteps */
+    float       pitch_bend;     /* pitch bending factor */
+    bool        mono;           /* whether patch is monophonic or not */
+    PatchBool   legato;         /* whether patch is played legato or not */
 
     PatchPlayMode   play_mode;  /* how this patch is to be played */
-    PatchParam      vol;        /* volume:                  [0.0, 1.0] */
+
+    PatchParam      amp;        /* amplitude:               [0.0, 1.0] */
     PatchParam      pan;        /* panning:                [-1.0, 1.0] */
     PatchParam      ffreq;      /* filter cutoff frequency: [0.0, 1.0] */
     PatchParam      freso;      /* filter resonance:        [0.0, 1.0] */
-    PatchParam      pitch;      /* pitch scaling:           [0.0, 1.0] */
+    PatchParam      pitch;      /* pitch scaling:          [-1.0, 1.0] */
 
     double mod_pitch_min[MAX_MOD_SLOTS];
     double mod_pitch_max[MAX_MOD_SLOTS];
@@ -97,33 +133,12 @@ struct _Patch
     LFOParams   glfo_params[PATCH_MAX_LFOS];
     LFOParams   vlfo_params[VOICE_MAX_LFOS];
 
-    /*  we need tables to store output values of global LFOs. there are
-        good reasons for this, it's a necessity and, it's false to think 
-        this places any limitations on the modulation of the global LFOs
-        (think: how would modulating a global LFO by a source from one of
-        the (many) voices work? (we pretend it's impossible for simplicity's
-        sake and therefor the right answer is it cannot work, and 
-        consequently there are no problems :-) ).
+    /*  use tables to store output values of global LFOs
     */
     float*      glfo_table[PATCH_MAX_LFOS];
 
-    /* NOTE: FIXME-ISH?
-        above statement falls apart if the patch is monophonic - there
-        would only ever be one voice and this makes it perfectly reasonable
-        to allow modulation of the global LFOs by a source within the
-        (one and only) voice.
-
-        the over-arching logic would be something along the lines of
-
-        if monophonic
-        then
-            don't use global lfo tables
-        endif
-    */
-
     ADSRParams  env_params[VOICE_MAX_ENVS];
 
-
     /* each patch is responsible for its own voices */
     PatchVoice* voices[PATCH_VOICE_COUNT];
     int         last_note;	/* the last MIDI note value that played us */
diff --git a/src/patch_private/patch_defs.c b/libpetrifoo/patch_private/patch_defs.c
similarity index 100%
rename from src/patch_private/patch_defs.c
rename to libpetrifoo/patch_private/patch_defs.c
diff --git a/src/patch_private/patch_defs.h b/libpetrifoo/patch_private/patch_defs.h
similarity index 100%
rename from src/patch_private/patch_defs.h
rename to libpetrifoo/patch_private/patch_defs.h
diff --git a/src/mod_src.h b/libpetrifoo/patch_private/patch_event_data.h
similarity index 50%
rename from src/mod_src.h
rename to libpetrifoo/patch_private/patch_event_data.h
index be67eda..00ce040 100644
--- a/src/mod_src.h
+++ b/libpetrifoo/patch_private/patch_event_data.h
@@ -18,32 +18,65 @@
 */
 
 
-#ifndef MOD_SRC_H
-#define MOD_SRC_H
+#ifndef PATCH_EVENT_DATA_H
+#define PATCH_EVENT_DATA_H
+
 
 #include <stdbool.h>
+#include <stdint.h>
+
+
+#define EV_MAX_IDS 4
+/* id[0] = patch_id*/
+
+struct _PatchEvent
+{
+    int id[EV_MAX_IDS];
+    
+
+}/* typedef'd in patch_event.h */;
+
+
+typedef struct _EventSetVar
+{
+    uint16_t type;
+    uint16_t size;
+    int patch_id;
+
+    int var_id;
+
+    union {
+        bool    b;
+        int     i;
+        float   f;
+        char*   data;
+    };
 
+} EventSetVar;
 
-#include "names.h"
 
+typedef struct _EventSetParam
+{
+    uint16_t type;
+    uint16_t size;
+    int patch_id;
 
-/* construct/destruct */
-void        mod_src_create(void);
-void        mod_src_destroy(void);
+    int     param;
+    float   value;
 
-/* get a list of modulation sources satisfying mod_src_bitmask */
-id_name*    mod_src_get(int mod_src_bitmask);
+} EventSetParam;
 
-/* free memory previously allocated by mod_src_get */
-void        mod_src_free(id_name*);
 
-/* get id of named mod src as long as it satisfies mod_src_bitmask */
-int         mod_src_id(const char*, int mod_src_bitmask);
+typedef struct _EventSetParMod
+{
+    uint16_t type;
+    uint16_t size;
+    int patch_id;
 
-const char* mod_src_name(int id);
+    int     param;
+    int     slot;
+    float   value;
 
-bool        mod_src_is_global(int id);
-bool        mod_src_maybe_eg(const char*);
-bool        mod_src_maybe_lfo(const char*);
+} EventSetParMod;
 
 #endif
diff --git a/src/patch_private/patch_macros.h b/libpetrifoo/patch_private/patch_macros.h
similarity index 59%
rename from src/patch_private/patch_macros.h
rename to libpetrifoo/patch_private/patch_macros.h
index d0cfa86..87440c9 100644
--- a/src/patch_private/patch_macros.h
+++ b/libpetrifoo/patch_private/patch_macros.h
@@ -26,31 +26,30 @@
 #define PATCH_MACROS_H
 
 
-#define INLINE_ISOK_DEF                                             \
-inline static int isok(int id)                                      \
-{                                                                   \
-    if (id < 0 || id >= PATCH_COUNT                                 \
-     || !patches[id] || !patches[id]->active)                       \
-        return 0;                                                   \
-    return 1;                                                       \
+#define INLINE_PATCHOK_DEF          \
+inline static bool patchok(int id)  \
+{                                   \
+    return (id >= 0 && id < PATCH_COUNT     \
+                    && patches[id] != 0     \
+                    && patches[id]->active);\
 }
 
 
-#define INLINE_PATCH_TRIGGER_GLOBAL_LFO_DEF                             \
-inline static void                                                      \
-patch_trigger_global_lfo(int patch_id, LFO* lfo, LFOParams* lfopar)     \
-{                                                                       \
-    Patch* p = patches[patch_id];                                       \
-    float const* src;                                                   \
-    src = patch_mod_id_to_pointer(lfopar->fm1_id, p, NULL);             \
-    lfo_set_fm1(lfo, src);                                              \
-    src = patch_mod_id_to_pointer(lfopar->fm2_id, p, NULL);             \
-    lfo_set_fm2(lfo, src);                                              \
-    src = patch_mod_id_to_pointer(lfopar->am1_id, p, NULL);             \
-    lfo_set_am1(lfo, src);                                              \
-    src = patch_mod_id_to_pointer(lfopar->am2_id, p, NULL);             \
-    lfo_set_am2(lfo, src);                                              \
-    lfo_rigger(lfo, lfopar);                                            \
+#define INLINE_PATCH_TRIGGER_GLOBAL_LFO_DEF                 \
+inline static void                                          \
+patch_trigger_global_lfo(int patch_id, LFO* lfo, LFOParams* lfopar) \
+{                                                           \
+    Patch* p = patches[patch_id];                           \
+    float const* src;                                       \
+    src = patch_mod_id_to_pointer(lfopar->fm1_id, p, NULL); \
+    lfo_set_fm1(lfo, src);                                  \
+    src = patch_mod_id_to_pointer(lfopar->fm2_id, p, NULL); \
+    lfo_set_fm2(lfo, src);                                  \
+    src = patch_mod_id_to_pointer(lfopar->am1_id, p, NULL); \
+    lfo_set_am1(lfo, src);                                  \
+    src = patch_mod_id_to_pointer(lfopar->am2_id, p, NULL); \
+    lfo_set_am2(lfo, src);                                  \
+    lfo_update_params(lfo, lfopar);                         \
 }
 
 
diff --git a/src/patch_private/patch_voice.c b/libpetrifoo/patch_private/patch_voice.c
similarity index 98%
rename from src/patch_private/patch_voice.c
rename to libpetrifoo/patch_private/patch_voice.c
index f421355..d1555a8 100644
--- a/src/patch_private/patch_voice.c
+++ b/libpetrifoo/patch_private/patch_voice.c
@@ -37,24 +37,31 @@ PatchVoice* patch_voice_new(void)
     pv->active =        false;
     pv->ticks =         0;
     pv->relset =        0;
+
     pv->relmode =       RELEASE_NONE;
+
     pv->released =      false;
     pv->to_end =        false;
+
     pv->dir =           0;
+
     pv->note =          0;
     pv->pitch =         0;
     pv->pitch_step =    0;
+
     pv->porta_ticks =   0;
+
     pv->posi =          0;
     pv->posf =          0;
     pv->stepi =         0;
     pv->stepf =         0;
+
     pv->vel =           0;
     pv->key_track =     0;
 
     for (i = 0; i < MAX_MOD_SLOTS; ++i)
     {
-        pv->vol_mod[i] = NULL;
+        pv->amp_mod[i] = NULL;
         pv->pan_mod[i] = NULL;
         pv->ffreq_mod[i] = NULL;
         pv->freso_mod[i] = NULL;
@@ -71,20 +78,24 @@ PatchVoice* patch_voice_new(void)
     pv->fbl =           0;
     pv->flr =           0;
     pv->fbr =           0;
+
     pv->playstate =     PLAYSTATE_OFF;
     pv->xfade =         false;
     pv->loop =          false;
+
     pv->fade_posi =     -1;
     pv->fade_posf =     0;
 
     pv->fade_out_start_pos =    0;
+
     pv->fade_declick =          0;
+
     pv->xfade_point_posi =      0;
     pv->xfade_point_posf =      0;
-
     pv->xfade_posi =    -1;
     pv->xfade_posf =    0;
     pv->xfade_dir =     0;
+
     pv->xfade_declick = 0;
 
     return pv;
diff --git a/src/patch_private/patch_voice.h b/libpetrifoo/patch_private/patch_voice.h
similarity index 97%
rename from src/patch_private/patch_voice.h
rename to libpetrifoo/patch_private/patch_voice.h
index 18659bb..dc76fbc 100644
--- a/src/patch_private/patch_voice.h
+++ b/libpetrifoo/patch_private/patch_voice.h
@@ -75,7 +75,11 @@ typedef struct _PatchVoice
     double      pitch;      /* what pitch ratio to play at */
     double      pitch_step; /* how much to increment pitch by each
                              * porta_tick */
+    bool        legato;
+    bool        portamento;
+    float       porta_secs;
     int         porta_ticks;/* how many ticks to increment pitch for */
+
     int         posi;       /* integer sample index */
     uint32_t    posf;       /* fractional sample index */
     int         stepi;      /* integer step amount */
@@ -84,7 +88,7 @@ typedef struct _PatchVoice
     float   vel;            /* velocity; volume of this voice */
     float   key_track;      /* = (note - lower) / (upper - lower) */
 
-    float const* vol_mod[MAX_MOD_SLOTS];
+    float const* amp_mod[MAX_MOD_SLOTS];
     float const* pan_mod[MAX_MOD_SLOTS];
     float const* ffreq_mod[MAX_MOD_SLOTS];
     float const* freso_mod[MAX_MOD_SLOTS];
diff --git a/libpetrifoo/patch_set_and_get.c b/libpetrifoo/patch_set_and_get.c
new file mode 100644
index 0000000..708907c
--- /dev/null
+++ b/libpetrifoo/patch_set_and_get.c
@@ -0,0 +1,1341 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a Specimen original, modified 2011
+*/
+
+
+#include "patch_set_and_get.h"
+
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+
+#include "sample.h"
+#include "adsr.h"
+#include "lfo.h"
+#include "pf_error.h"
+
+
+#include "patch_private/patch_data.h"
+#include "patch_private/patch_defs.h"
+#include "patch_private/patch_macros.h"
+
+
+INLINE_PATCHOK_DEF
+
+inline static bool markok(int id)
+{
+    return (id >= WF_MARK_START && id <= WF_MARK_STOP);
+}
+
+
+inline static bool marksetok(int id)
+{
+    return (id >= WF_MARK_PLAY_START && id <= WF_MARK_PLAY_STOP);
+}
+
+
+static inline void set_mark_frame(int patch_id, int mark, int frame)
+{
+    *(patches[patch_id]->marks[mark]) = frame;
+}
+
+
+static inline int get_mark_frame(int patch_id, int mark)
+{
+    return *(patches[patch_id]->marks[mark]);
+}
+
+
+static int get_mark_frame_range(int patch_id, int mark, int* min, int* max)
+{
+    int xfade;
+
+    assert(patchok(patch_id));
+    assert(markok(mark));
+
+    xfade = patches[patch_id]->xfade_samples;
+
+    if (mark == WF_MARK_START || mark == WF_MARK_STOP)
+    {
+        *min = *max = get_mark_frame(patch_id, mark);
+        /* indicate non-editable! */
+        return -1;
+    }
+
+    /* potential range: */
+    *min = get_mark_frame(patch_id, mark - 1);
+    *max = get_mark_frame(patch_id, mark + 1);
+
+    /* tweak if necessary */
+    switch(mark)
+    {
+    case WF_MARK_PLAY_START:
+        *max -= xfade;
+        break;
+    case WF_MARK_PLAY_STOP:
+        *min += xfade;
+        break;
+    case WF_MARK_LOOP_START:
+        *min += xfade;
+        *max -= xfade;
+        break;
+    case WF_MARK_LOOP_STOP:
+        *min += xfade;
+        *max -= xfade;
+        break;
+    default:
+        assert(0);
+    }
+
+    return get_mark_frame(patch_id, mark);
+}
+
+
+static PatchParam* get_patch_param(int patch_id, PatchParamType param)
+{
+    switch(param)
+    {
+    case PATCH_PARAM_AMPLITUDE: return &patches[patch_id]->amp;
+    case PATCH_PARAM_PANNING:   return &patches[patch_id]->pan;
+    case PATCH_PARAM_CUTOFF:    return &patches[patch_id]->ffreq;
+    case PATCH_PARAM_RESONANCE: return &patches[patch_id]->freso;
+    case PATCH_PARAM_PITCH:     return &patches[patch_id]->pitch;
+    default:
+        assert(0);
+    }
+    return 0;
+}
+
+
+static PatchBool* get_patch_bool(int patch_id, PatchBoolType booltype)
+{
+    assert(patchok(patch_id));
+    switch(booltype)
+    {
+    case PATCH_BOOL_PORTAMENTO: return &patches[patch_id]->porta;
+    case PATCH_BOOL_LEGATO:     return &patches[patch_id]->legato;
+    default:
+        assert(0);
+    }
+    return 0;
+}
+
+
+static PatchFloat* get_patch_float(int patch_id, PatchFloatType floattype)
+{
+    assert(patchok(patch_id));
+    switch(floattype)
+    {
+    case PATCH_FLOAT_PORTAMENTO_TIME:
+        return &patches[patch_id]->porta_secs;
+    default:
+        assert(0);
+    }
+    return 0;
+}
+
+
+/* inline static function def macro, see private/patch_data.h */
+INLINE_PATCH_TRIGGER_GLOBAL_LFO_DEF
+
+
+
+/**************************************************************************/
+/************************* ENVELOPE SETTERS *******************************/
+/**************************************************************************/
+
+inline static int mod_src_to_eg_index(int id)
+{
+    assert(id >= MOD_SRC_EG);
+    id -= MOD_SRC_EG;
+    assert(id < VOICE_MAX_ENVS);
+    return id;
+}
+
+
+int patch_set_env_active(int patch_id, int eg, bool state)
+{
+    assert(patchok(patch_id));
+    eg = mod_src_to_eg_index(eg);
+    patches[patch_id]->env_params[eg].active = state;
+    return 0;
+}
+
+
+#define PATCH_SET_ENV_TIME( _EGPAR )                            \
+int patch_set_env_##_EGPAR(int patch_id, int eg, float secs)    \
+{                                                       \
+    assert(patchok(patch_id));                          \
+    if (secs < 0.0f)                                    \
+    {                                                   \
+        pf_error(PF_ERR_PATCH_VALUE_NEGATIVE);          \
+        return -1;                                      \
+    }                                                   \
+    eg = mod_src_to_eg_index(eg);                       \
+    patches[patch_id]->env_params[eg]._EGPAR = secs;    \
+    return 0;                                           \
+}
+
+PATCH_SET_ENV_TIME( delay  )
+PATCH_SET_ENV_TIME( attack )
+PATCH_SET_ENV_TIME( hold   )
+PATCH_SET_ENV_TIME( decay  )
+
+/* special cases: */
+
+int patch_set_env_sustain (int patch_id, int eg, float level)
+{
+    assert(patchok(patch_id));
+    if (level < 0.0 || level > 1.0)
+    {
+        pf_error(PF_ERR_PATCH_VALUE_LEVEL);
+        return -1;
+    }
+    eg = mod_src_to_eg_index(eg);
+    patches[patch_id]->env_params[eg].sustain = level;
+    return 0;
+}
+
+
+int patch_set_env_release (int patch_id, int eg, float secs)
+{
+    assert(patchok(patch_id));
+    if (secs < 0.0f)
+    {
+        pf_error(PF_ERR_PATCH_VALUE_NEGATIVE);
+        return -1;
+    }
+    eg = mod_src_to_eg_index(eg);
+    /* use of min release time should remain hidden */
+    if (secs < PATCH_MIN_RELEASE)
+        secs = PATCH_MIN_RELEASE;
+    patches[patch_id]->env_params[eg].release = secs;
+    return 0;
+}
+
+
+int patch_set_env_key_amt(int patch_id, int eg, float val)
+{
+    assert(patchok(patch_id));
+    if (val < 0.0 || val > 1.0)
+    {
+        pf_error(PF_ERR_PATCH_VALUE_LEVEL);
+        return -1;
+    }
+    eg = mod_src_to_eg_index(eg);
+    patches[patch_id]->env_params[eg].key_amt = val;
+    return 0;
+}
+
+
+/**************************************************************************/
+/************************* ENVELOPE GETTERS *******************************/
+/**************************************************************************/
+
+#define PATCH_GET_ENV_PARAM( _EGPAR, _EGPARTYPE)        \
+_EGPARTYPE patch_get_env_##_EGPAR(int patch_id, int eg) \
+{                                                       \
+    assert(patchok(patch_id));                             \
+    eg = mod_src_to_eg_index(eg);                       \
+    return patches[patch_id]->env_params[eg]._EGPAR;    \
+}
+
+PATCH_GET_ENV_PARAM( active,    bool  )
+PATCH_GET_ENV_PARAM( delay,     float )
+PATCH_GET_ENV_PARAM( attack,    float )
+PATCH_GET_ENV_PARAM( hold,      float )
+PATCH_GET_ENV_PARAM( decay,     float )
+PATCH_GET_ENV_PARAM( sustain,   float )
+PATCH_GET_ENV_PARAM( key_amt,   float )
+
+/* special cases: */
+float patch_get_env_release (int patch_id, int eg)
+{
+    float val;
+    assert(patchok(patch_id));
+    eg = mod_src_to_eg_index(eg);
+    val = patches[patch_id]->env_params[eg].release;
+    /* hide usage of min-release-value from outside world */
+    if (val <= PATCH_MIN_RELEASE)
+        val = 0;
+    return val;
+}
+
+
+/************************************************************************/
+/*************************** LFO SETTERS ********************************/
+/************************************************************************/
+
+static LFOParams* lfopar_from_id(int patch_id, int id, LFO** lfo)
+{
+    assert(patchok(patch_id));
+    assert( ((id & MOD_SRC_VLFO) && (id & MOD_SRC_GLFO)) == 0);
+    assert( ((id & MOD_SRC_VLFO) || (id & MOD_SRC_GLFO)) != 0);
+
+    if (lfo)
+        *lfo = 0;
+
+    if (id & MOD_SRC_VLFO)
+    {
+        assert ((id -= MOD_SRC_VLFO) < VOICE_MAX_LFOS);
+        return &patches[patch_id]->vlfo_params[id];
+    }
+
+    assert((id -= MOD_SRC_GLFO) < PATCH_MAX_LFOS);
+
+    if (lfo)
+        *lfo = patches[patch_id]->glfo[id];
+
+    return &patches[patch_id]->glfo_params[id];
+}
+
+
+#define PATCH_SET_LFO_VAR( _LFOVAR, _LFOVARTYPE )                       \
+int patch_set_lfo_##_LFOVAR(int patch_id, int lfo_id, _LFOVARTYPE val)  \
+{                                                   \
+    LFO*        lfo;                                \
+    LFOParams*  lfopar;                             \
+    lfopar = lfopar_from_id(patch_id, lfo_id, &lfo);\
+    lfopar->_LFOVAR = val;                          \
+    if (lfo)                                        \
+        lfo_update_params(lfo, lfopar);             \
+    return 0;                                       \
+}
+
+PATCH_SET_LFO_VAR( active,   bool)
+PATCH_SET_LFO_VAR( positive, bool)
+PATCH_SET_LFO_VAR( shape,    LFOShape)
+PATCH_SET_LFO_VAR( sync,     bool)
+
+
+#define PATCH_SET_LFO_VAR_BOUNDED( _LFOVAR, _LFOVARTYPE )               \
+int patch_set_lfo_##_LFOVAR(int patch_id, int lfo_id, _LFOVARTYPE val)  \
+{                                                   \
+    LFO*        lfo;                                \
+    LFOParams*  lfopar;                             \
+    lfopar = lfopar_from_id(patch_id, lfo_id, &lfo);\
+    if (val < 0.0)                                  \
+    {                                               \
+        pf_error(PF_ERR_PATCH_VALUE_NEGATIVE);      \
+        return -1;                                  \
+    }                                               \
+    lfopar->_LFOVAR = val;                          \
+    if (lfo)                                        \
+        lfo_update_params(lfo, lfopar);             \
+    return 0;                                       \
+}
+
+PATCH_SET_LFO_VAR_BOUNDED( attack,     float )
+PATCH_SET_LFO_VAR_BOUNDED( sync_beats, float )
+PATCH_SET_LFO_VAR_BOUNDED( delay,      float )
+PATCH_SET_LFO_VAR_BOUNDED( freq,       float )
+
+
+/************************************************************************/
+/*************************** LFO GETTERS ********************************/
+/************************************************************************/
+
+#define PATCH_GET_LFO_VAR( _LFOVAR, _LFOVARTYPE )               \
+_LFOVARTYPE patch_get_lfo_##_LFOVAR(int patch_id, int lfo_id)   \
+{                                                               \
+    return lfopar_from_id(patch_id, lfo_id, NULL)->_LFOVAR;     \
+}
+
+PATCH_GET_LFO_VAR( active,     bool )
+PATCH_GET_LFO_VAR( attack,     float )
+PATCH_GET_LFO_VAR( sync_beats, float )
+PATCH_GET_LFO_VAR( delay,      float )
+PATCH_GET_LFO_VAR( freq,       float )
+PATCH_GET_LFO_VAR( positive,   bool )
+PATCH_GET_LFO_VAR( shape,      LFOShape )
+PATCH_GET_LFO_VAR( sync,       bool )
+
+/**************************************************************************/
+/************************ PARAMETER SETTERS *******************************/
+/**************************************************************************/
+
+/* sets the cut signal this patch emits when activated */
+int patch_set_cut (int patch_id, int cut)
+{
+    assert(patchok(patch_id));
+    patches[patch_id]->cut = cut;
+    return 0;
+}
+
+/* sets the cut signal that terminates this patch if active */
+int patch_set_cut_by (int patch_id, int cut_by)
+{
+    assert(patchok(patch_id));
+    patches[patch_id]->cut_by = cut_by;
+    return 0;
+}
+
+/* set whether this patch should be played legato or not */
+int patch_set_legato(int patch_id, bool val)
+{
+    assert(patchok(patch_id));
+    patches[patch_id]->legato.active = val;
+    return 0;
+}
+
+
+int patch_set_fade_samples(int patch_id, int samples)
+{
+    assert(patchok(patch_id));
+
+    if (patches[patch_id]->sample->sp == NULL)
+        return 0;
+
+    if (samples < 0)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+
+    if (patches[patch_id]->play_start + samples * 2
+        >= patches[patch_id]->play_stop)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+
+    patches[patch_id]->fade_samples = samples;
+    return 0;
+}
+
+int patch_set_xfade_samples(int patch_id, int samples)
+{
+    assert(patchok(patch_id));
+
+    if (patches[patch_id]->sample->sp == NULL)
+        return 0;
+
+    if (samples < 0)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+
+    if (patches[patch_id]->loop_start + samples
+      > patches[patch_id]->loop_stop)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+
+    if (patches[patch_id]->loop_stop + samples
+      > patches[patch_id]->play_stop)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+
+    patches[patch_id]->xfade_samples = samples;
+    return 0;
+}
+
+
+int patch_set_mark_frame(int patch_id, int mark, int frame)
+{
+    /* FIXME: perhaps this complexity better placed in libpetrifui? */
+    assert(patchok(patch_id));
+    assert(marksetok(mark));
+
+    if (patches[patch_id]->sample->sp == NULL)
+    {   /* FIXME: assert here? */
+        errmsg("sample not set\n");
+        return -1;
+    }
+
+/*    if (!mark_settable(mark))
+    {
+        debug("mark %d not settable\n", mark);
+        return -1;
+    }
+*/
+    int min;
+    int max;
+
+    get_mark_frame_range(patch_id, mark, &min, &max);
+
+    if (frame < min || frame > max)
+        return -1;
+
+    set_mark_frame(patch_id, mark, frame);
+    return mark;
+}
+
+
+int patch_set_mark_frame_expand(int patch_id, int mark, int frame,
+                                                     int* also_changed)
+{
+    int also = 0;
+    int also_frame = -1;
+    int xfade = patches[patch_id]->xfade_samples;
+    int fade = patches[patch_id]->fade_samples;
+
+    assert(patchok(patch_id));
+
+    if (patches[patch_id]->sample->sp == NULL)
+        return -1;
+
+    /*  if callee wishes not to be informed about which marks get changed
+        as a result of changing this one, also_changed will be NULL. It
+        needs to be a valid pointer.
+     */
+    if (!also_changed)
+        also_changed = &also;
+
+    *also_changed = -1;
+
+    switch(mark)
+    {
+    case WF_MARK_PLAY_START:
+        also_frame = frame + xfade; /* pot loop start pos */
+
+        if (frame + fade * 2 >= get_mark_frame(patch_id, WF_MARK_PLAY_STOP)
+         || also_frame >= get_mark_frame(patch_id, WF_MARK_LOOP_STOP))
+        {
+            mark = -1;
+        }
+        else if (also_frame > get_mark_frame(patch_id, WF_MARK_LOOP_START))
+        {   /* moving play start along pushes loop start along... */
+            if (also_frame + xfade
+                < get_mark_frame(patch_id, WF_MARK_LOOP_STOP))
+            {
+                *also_changed = WF_MARK_LOOP_START;
+            }
+            else
+                mark = -1;
+        }
+        break;
+
+    case WF_MARK_PLAY_STOP:
+        also_frame = frame - xfade; /* pot loop stop pos */
+
+        if (frame - fade * 2<= get_mark_frame(patch_id, WF_MARK_PLAY_START)
+         || also_frame <= get_mark_frame(patch_id, WF_MARK_LOOP_START))
+        {
+            mark = -1;
+        }
+        else if (also_frame < get_mark_frame(patch_id, WF_MARK_LOOP_STOP))
+        {
+            if (also_frame - xfade
+                > get_mark_frame(patch_id, WF_MARK_LOOP_START))
+            {
+                *also_changed = WF_MARK_LOOP_STOP;
+            }
+            else
+                mark = -1;
+        }
+        break;
+
+    case WF_MARK_LOOP_START:
+        also_frame = frame - xfade; /* pot play start pos */
+
+        if (frame + xfade >= get_mark_frame(patch_id, WF_MARK_LOOP_STOP))
+            mark = -1;
+        else if (also_frame < get_mark_frame(patch_id, WF_MARK_PLAY_START))
+        {
+            if (also_frame > 0)
+                *also_changed = WF_MARK_PLAY_START;
+            else
+                mark = -1;
+        }
+        break;
+
+    case WF_MARK_LOOP_STOP:
+        also_frame = frame + xfade /* pot play stop pos */;
+
+        if (frame - xfade <= get_mark_frame(patch_id, WF_MARK_LOOP_START))
+            mark = -1;
+        else if (also_frame > get_mark_frame(patch_id, WF_MARK_PLAY_STOP))
+        {
+            if (also_frame < get_mark_frame(patch_id, WF_MARK_STOP))
+                *also_changed = WF_MARK_PLAY_STOP;
+            else
+                mark = -1;
+        }
+        break;
+
+    default:
+        mark = -1;
+    }
+
+    if (mark != -1)
+    {
+        set_mark_frame(patch_id, mark, frame);
+    }
+
+    if (*also_changed != -1)
+    {
+        set_mark_frame(patch_id, *also_changed, also_frame);
+    }
+
+    return mark;
+}
+
+
+/* sets the name */
+int patch_set_name (int patch_id, const char *name)
+{
+    assert(patchok(patch_id));
+    strncpy (patches[patch_id]->name, name, PATCH_MAX_NAME);
+    return 0;
+}
+
+
+#define PATCH_SET_VAR( _VAR, _VARMIN, _VARMAX ) \
+int patch_set_##_VAR(int patch_id, int val)     \
+{                                               \
+    assert(patchok(patch_id));                     \
+    if (val < _VARMIN || val > _VARMAX)         \
+    {                                           \
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);     \
+        return -1;                              \
+    }                                           \
+    patches[patch_id]->_VAR = val;              \
+    return 0;                                   \
+}
+
+PATCH_SET_VAR( channel,     0,  15 )
+PATCH_SET_VAR( root_note,   0,  127 )
+PATCH_SET_VAR( lower_note,  0,  127 )
+PATCH_SET_VAR( upper_note,  0,  127 )
+PATCH_SET_VAR( lower_vel,   0,  127 )
+PATCH_SET_VAR( upper_vel,   0,  127 )
+
+PATCH_SET_VAR( pitch_steps, -PATCH_MAX_PITCH_STEPS, PATCH_MAX_PITCH_STEPS )
+
+/* set whether the patch is monophonic or not */
+int patch_set_monophonic(int patch_id, bool val)
+{
+    assert(patchok(patch_id));
+    patches[patch_id]->mono = val;
+    return 0;
+}
+
+
+#define PATCH_SET_PARAM( _PARAM, _PARMIN, _PARMAX ) \
+int patch_set_##_PARAM(int patch_id, float val)     \
+{                                                   \
+    assert(patchok(patch_id));                         \
+    if (val < _PARMIN || val > _PARMAX)             \
+    {                                               \
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);         \
+        return -1;                                  \
+    }                                               \
+    patches[patch_id]->_PARAM.val = val;            \
+    return 0;                                       \
+}
+
+PATCH_SET_PARAM( amp,       0.0,    1.0 )
+PATCH_SET_PARAM( pan,      -1.0,    1.0 )
+PATCH_SET_PARAM( ffreq,     0.0,    1.0 )
+PATCH_SET_PARAM( freso,     0.0,    1.0 )
+PATCH_SET_PARAM( pitch,    -1.0,    1.0 )
+
+
+/* sets the play mode */
+int patch_set_play_mode (int patch_id, PatchPlayMode mode)
+{
+    assert(patchok(patch_id));
+
+    if (mode & PATCH_PLAY_SINGLESHOT)
+    {
+        assert( ((mode & PATCH_PLAY_TRIM)
+               | (mode & PATCH_PLAY_LOOP)) == 0);
+    }
+    else if (mode & PATCH_PLAY_TRIM)
+    {
+        assert( ((mode & PATCH_PLAY_SINGLESHOT)
+               | (mode & PATCH_PLAY_LOOP)) == 0);
+    }
+    else if (mode & PATCH_PLAY_LOOP)
+    {
+        assert( ((mode & PATCH_PLAY_SINGLESHOT)
+               | (mode & PATCH_PLAY_TRIM)) == 0);
+    }
+
+    if (!(mode & PATCH_PLAY_LOOP))
+    {
+        assert( ((mode & PATCH_PLAY_PINGPONG)
+               | (mode & PATCH_PLAY_TO_END)) == 0);
+    }
+
+    patches[patch_id]->play_mode = mode;
+    return 0;
+}
+
+/* set whether portamento is being used or not */
+int patch_set_portamento (int patch_id, bool val)
+{
+    assert(patchok(patch_id));
+    patches[patch_id]->porta.active = val;
+    return 0;
+}
+
+/* set length of portamento slides in seconds */
+int patch_set_portamento_time (int patch_id, float secs)
+{
+    assert(patchok(patch_id));
+    if (secs < 0.0)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+    patches[patch_id]->porta_secs.val = secs;
+    return 0;
+}
+
+
+
+/**************************************************************************/
+/************************* PARAMETER GETTERS*******************************/
+/**************************************************************************/
+
+
+#define PATCH_GET_VAR( _VAR )       \
+int patch_get_##_VAR(int patch_id)  \
+{                                   \
+    assert(patchok(patch_id));         \
+    return patches[patch_id]->_VAR; \
+}
+
+PATCH_GET_VAR( channel )
+PATCH_GET_VAR( cut )
+PATCH_GET_VAR( cut_by )
+PATCH_GET_VAR( display_index )
+PATCH_GET_VAR( root_note )
+PATCH_GET_VAR( lower_note )
+PATCH_GET_VAR( upper_note )
+PATCH_GET_VAR( lower_vel )
+PATCH_GET_VAR( upper_vel )
+PATCH_GET_VAR( pitch_steps )
+
+
+
+/* get the filter cutoff value */
+float patch_get_cutoff(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->ffreq.val;
+}
+
+
+/* get the number of frame in the sample */
+int patch_get_frames(int patch_id)
+{
+    assert(patchok(patch_id));
+    if (patches[patch_id]->sample->sp == NULL)
+        return 0;
+    return patches[patch_id]->sample->frames;
+}
+
+/* get whether this patch is played legato or not */
+bool patch_get_legato(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->legato.active;
+}
+
+
+int patch_get_mark_frame(int patch_id, int mark)
+{
+    assert(patchok(patch_id));
+    assert(markok(mark));
+
+    /* FIXME: should this be an assert or not ? */
+    assert(patches[patch_id]->sample->sp != NULL);
+
+    return get_mark_frame(patch_id, mark);
+}
+
+
+int patch_get_mark_frame_range(int patch_id, int mark, int* frame_min,
+                                                    int* frame_max)
+{
+    assert(patchok(patch_id));
+    assert(markok(mark));
+
+    /* FIXME: should this be an assert or not ? */
+    assert(patches[patch_id]->sample->sp != NULL);
+
+    return get_mark_frame_range(patch_id, mark, frame_min, frame_max);
+}
+
+/* get whether this patch is monophonic or not */
+bool patch_get_monophonic(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->mono;
+}
+
+/* get the name */
+char *patch_get_name(int patch_id)
+{
+    char *name;
+    assert(patchok(patch_id));
+    name = strdup (patches[patch_id]->name);
+    return name;
+}
+
+
+/* get the panorama */
+float patch_get_panning(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->pan.val;
+}
+
+/* get the pitch */
+float patch_get_pitch(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->pitch.val;
+}
+
+/* get the play mode */
+PatchPlayMode patch_get_play_mode(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->play_mode;
+}
+
+/* get whether portamento is used or not */
+bool patch_get_portamento(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->porta.active;
+}
+
+/* get length of portamento slides in seconds */
+float patch_get_portamento_time(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->porta_secs.val;
+}
+
+
+/* get the filter's resonance amount */
+float patch_get_resonance(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->freso.val;
+}
+
+/* get a pointer to the sample data */
+const float *patch_get_sample(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->sample->sp;
+}
+
+/* get the name of the sample file */
+const char *patch_get_sample_name(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->sample->filename;
+}
+
+
+/* get the amplitude */
+float patch_get_amplitude(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->amp.val;
+}
+
+
+int patch_get_fade_samples(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->fade_samples;
+}
+
+
+int patch_get_xfade_samples(int patch_id)
+{
+    assert(patchok(patch_id));
+    return patches[patch_id]->xfade_samples;
+}
+
+
+int patch_get_max_fade_samples(int patch_id)
+{
+    assert(patchok(patch_id));
+    return (patches[patch_id]->play_stop - patches[patch_id]->play_start) / 2;
+}
+
+
+int patch_get_max_xfade_samples(int patch_id)
+{
+    int min;
+    int tmp;
+
+    assert(patchok(patch_id));
+
+    min = patches[patch_id]->sample->frames;
+
+    tmp = patches[patch_id]->loop_stop - patches[patch_id]->loop_start;
+    min = (tmp < min) ? tmp : min;
+
+    tmp = patches[patch_id]->play_stop - patches[patch_id]->loop_stop;
+    min = (tmp < min) ? tmp : min;
+
+    tmp = patches[patch_id]->loop_start - patches[patch_id]->play_start;
+    min = (tmp < min) ? tmp : min;
+
+    return min;
+}
+
+
+/******************************************************************/
+/*************************** PARAM ********************************/
+/******************************************************************/
+
+float patch_param_get_value(int patch_id, PatchParamType param)
+{
+    assert(patchok(patch_id));
+
+    switch(param)
+    {
+    case PATCH_PARAM_AMPLITUDE: return patches[patch_id]->amp.val;
+    case PATCH_PARAM_PANNING:   return patches[patch_id]->pan.val;
+    case PATCH_PARAM_CUTOFF:    return patches[patch_id]->ffreq.val;
+    case PATCH_PARAM_RESONANCE: return patches[patch_id]->freso.val;
+    case PATCH_PARAM_PITCH:     return patches[patch_id]->pitch.val;
+    default:
+        assert(0);
+    }
+}
+
+
+void patch_param_set_value(int patch_id, PatchParamType param, float v)
+{
+    assert(patchok(patch_id));
+
+    switch(param)
+    {
+    case PATCH_PARAM_AMPLITUDE: patches[patch_id]->amp.val = v;     break;
+    case PATCH_PARAM_PANNING:   patches[patch_id]->pan.val = v;     break;
+    case PATCH_PARAM_CUTOFF:    patches[patch_id]->ffreq.val = v;   break;
+    case PATCH_PARAM_RESONANCE: patches[patch_id]->freso.val = v;   break;
+    case PATCH_PARAM_PITCH:     patches[patch_id]->pitch.val = v;   break;
+    default:
+        assert(0);
+    }
+}
+
+
+/**************************************************************************/
+/*********************** MODULATION SETTERS *******************************/
+/**************************************************************************/
+
+#define PATCH_PARAM_CHECKS  \
+    PatchParam* p;          \
+    assert(patchok(patch_id)); \
+    p = get_patch_param(patch_id, param);
+
+int
+patch_param_set_mod_src(int patch_id, PatchParamType param, int slot,
+                                                            int id)
+{
+    PATCH_PARAM_CHECKS
+    assert(slot >=0 && slot <= MAX_MOD_SLOTS);
+    p->mod_id[slot] = id;
+    return 0;
+}
+
+
+int
+patch_param_set_mod_amt(int patch_id, PatchParamType param, int slot,
+                                                            float amt)
+{
+    PATCH_PARAM_CHECKS
+    assert(slot >=0 && slot <= MAX_MOD_SLOTS);
+
+    if (amt < -1.0 || amt > 1.0)
+    {
+        pf_error(PF_ERR_PATCH_PARAM_VALUE);
+        return -1;
+    }
+
+    p->mod_amt[slot] = amt;
+
+    if (param == PATCH_PARAM_PITCH)
+    {
+        patches[patch_id]->mod_pitch_max[slot] =
+                        pow(2, (amt * PATCH_MAX_PITCH_STEPS) / 12.0);
+        patches[patch_id]->mod_pitch_min[slot] =
+                        pow(2, -(amt * PATCH_MAX_PITCH_STEPS) / 12.0);
+    }
+
+    return 0;
+}
+
+
+#define PATCH_PARAM_SET_AMOUNT( _PARAM )            \
+int patch_param_set_##_PARAM##_amount               \
+    (int patch_id, PatchParamType param, float amt) \
+{                                           \
+    PATCH_PARAM_CHECKS                      \
+    if (amt < -1.0 || amt > 1.0)            \
+    {                                       \
+        pf_error(PF_ERR_PATCH_PARAM_VALUE); \
+        return -1;          \
+    }                       \
+    p->_PARAM##_amt = amt;  \
+    return 0;               \
+}
+
+PATCH_PARAM_SET_AMOUNT( vel )
+PATCH_PARAM_SET_AMOUNT( key )
+
+
+
+/**************************************************************************/
+/********************** MODULATION GETTERS ********************************/
+/**************************************************************************/
+
+int patch_param_get_mod_src(int patch_id, PatchParamType param, int slot)
+{
+    PATCH_PARAM_CHECKS
+    assert(slot >=0 && slot <= MAX_MOD_SLOTS);
+    return p->mod_id[slot];
+}
+
+
+float patch_param_get_mod_amt(int patch_id, PatchParamType param, int slot)
+{
+    PATCH_PARAM_CHECKS
+    assert(slot >=0 && slot <= MAX_MOD_SLOTS);
+    return p->mod_amt[slot];
+}
+
+
+float patch_param_get_vel_amount(int patch_id, PatchParamType param)
+{
+    PATCH_PARAM_CHECKS
+    return p->vel_amt;
+}
+
+
+float patch_param_get_key_amount(int patch_id, PatchParamType param)
+{
+    PATCH_PARAM_CHECKS
+    return p->key_amt;
+}
+
+
+#define PATCH_BOOL_GET                      \
+    PatchBool* b;                           \
+    assert(patchok(patch_id));                 \
+    b = get_patch_bool(patch_id, booltype); \
+
+
+/* PatchBool set/get */
+void patch_bool_set_active(int patch_id, PatchBoolType booltype, bool val)
+{
+    PATCH_BOOL_GET
+    b->active = val;
+}
+
+
+void patch_bool_set_thresh(int patch_id, PatchBoolType booltype, float val)
+{
+    PATCH_BOOL_GET
+    b->thresh = val;
+}
+
+
+void patch_bool_set_mod_src(int patch_id, PatchBoolType booltype,int mod_id)
+{
+    PATCH_BOOL_GET
+    b->mod_id = mod_id;
+}
+
+
+void patch_bool_get_all(int patch_id, PatchBoolType booltype,
+                        bool* active, float* thresh, int* mod_id)
+{
+    PATCH_BOOL_GET
+    *active = b->active;
+    *thresh = b->thresh;
+    *mod_id = b->mod_id;
+}
+
+
+bool patch_bool_get_active(int patch_id, PatchBoolType booltype)
+{
+    PATCH_BOOL_GET
+    return b->active;
+}
+
+
+float patch_bool_get_thresh(int patch_id, PatchBoolType booltype)
+{
+    PATCH_BOOL_GET
+    return b->thresh;
+}
+
+
+int patch_bool_get_mod_src(int patch_id, PatchBoolType booltype)
+{
+    PATCH_BOOL_GET
+    return b->mod_id;
+}
+
+#define PATCH_FLOAT_GET                     \
+    PatchFloat* f;                          \
+    assert(patchok(patch_id));                 \
+    f = get_patch_float(patch_id, floattype);
+
+
+/* PatchFloat set/get */
+void
+patch_float_set_value(int patch_id, PatchFloatType floattype, float val)
+{
+    PATCH_FLOAT_GET
+    f->val = val;
+}
+
+
+void
+patch_float_set_mod_amt(int patch_id, PatchFloatType floattype, float val)
+{
+    PATCH_FLOAT_GET
+    f->mod_amt = val;
+}
+
+
+void
+patch_float_set_mod_src(int patch_id, PatchFloatType floattype, int mod_id)
+{
+    PATCH_FLOAT_GET
+    f->mod_id = mod_id;
+}
+
+
+void patch_float_get_all(int patch_id, PatchFloatType floattype,
+                        float* value, float* mod_amt, int* mod_id)
+{
+    PATCH_FLOAT_GET
+    *value =    f->val;
+    *mod_amt =  f->mod_amt;
+    *mod_id =   f->mod_id;
+}
+
+
+float patch_float_get_value(int patch_id, PatchFloatType floattype)
+{
+    PATCH_FLOAT_GET
+    return f->val;
+}
+
+
+float patch_float_get_mod_amt(int patch_id, PatchFloatType floattype)
+{
+    PATCH_FLOAT_GET
+    return f->mod_amt;
+}
+
+
+int patch_float_get_mod_src(int patch_id, PatchFloatType floattype)
+{
+    PATCH_FLOAT_GET
+    return f->mod_id;
+}
+
+
+
+/**************************************************************************/
+/****************** LFO FREQ MODULATION SETTERS ***************************/
+/**************************************************************************/
+
+#define PATCH_LFO_CHECKS                                    \
+    LFO* lfo;                                               \
+    LFOParams* lfopar;                                      \
+    if (!(lfopar = lfopar_from_id(patch_id, lfo_id, &lfo))) \
+        return -1;
+
+#define PATCH_NULL_LFO_CHECKS                               \
+    LFOParams* lfopar;                                      \
+    if (!(lfopar = lfopar_from_id(patch_id, lfo_id, NULL))) \
+        return -1;
+
+
+int patch_set_lfo_fm1_src(int patch_id, int lfo_id, int modsrc_id)
+{
+    PATCH_LFO_CHECKS
+    lfopar->fm1_id = modsrc_id;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+int patch_set_lfo_fm2_src(int patch_id, int lfo_id, int modsrc_id)
+{
+    PATCH_LFO_CHECKS
+    lfopar->fm2_id = modsrc_id;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+int patch_set_lfo_fm1_amt(int patch_id, int lfo_id, float amount)
+{
+    PATCH_LFO_CHECKS
+    lfopar->fm1_amt = amount;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+int patch_set_lfo_fm2_amt(int patch_id, int lfo_id, float amount)
+{
+    PATCH_LFO_CHECKS
+    lfopar->fm2_amt = amount;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+
+
+/**************************************************************************/
+/****************** LFO FREQ MODULATION GETTERS ***************************/
+/**************************************************************************/
+
+int patch_get_lfo_fm1_src(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->fm1_id;
+}
+
+int patch_get_lfo_fm2_src(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->fm2_id;
+}
+
+float patch_get_lfo_fm1_amt(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->fm1_amt;
+}
+
+float patch_get_lfo_fm2_amt(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->fm2_amt;
+}
+
+
+/**************************************************************************/
+/******************* LFO AMP MODULATION SETTERS ***************************/
+/**************************************************************************/
+
+int patch_set_lfo_am1_src(int patch_id, int lfo_id, int modsrc_id)
+{
+    PATCH_LFO_CHECKS
+    lfopar->am1_id = modsrc_id;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+int patch_set_lfo_am2_src(int patch_id, int lfo_id, int modsrc_id)
+{
+    PATCH_LFO_CHECKS
+    lfopar->am2_id = modsrc_id;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+int patch_set_lfo_am1_amt(int patch_id, int lfo_id, float amount)
+{
+    PATCH_LFO_CHECKS
+    lfopar->am1_amt = amount;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+int patch_set_lfo_am2_amt(int patch_id, int lfo_id, float amount)
+{
+    PATCH_LFO_CHECKS
+    lfopar->am2_amt = amount;
+
+    if (lfo)
+        patch_trigger_global_lfo(patch_id, lfo, lfopar);
+
+    return 0;
+}
+
+
+
+/**************************************************************************/
+/******************* LFO AMP MODULATION GETTERS ***************************/
+/**************************************************************************/
+
+int patch_get_lfo_am1_src(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->am1_id;
+}
+
+int patch_get_lfo_am2_src(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->am2_id;
+}
+
+float patch_get_lfo_am1_amt(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->am1_amt;
+}
+
+float patch_get_lfo_am2_amt(int patch_id, int lfo_id)
+{
+    PATCH_NULL_LFO_CHECKS
+    return lfopar->am2_amt;
+}
+
+
diff --git a/libpetrifoo/patch_set_and_get.h b/libpetrifoo/patch_set_and_get.h
new file mode 100644
index 0000000..e480e3c
--- /dev/null
+++ b/libpetrifoo/patch_set_and_get.h
@@ -0,0 +1,245 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a Specimen original, modified 2011
+*/
+
+
+#ifndef __PATCH_SET_AND_GET_H__
+#define __PATCH_SET_AND_GET_H__
+
+
+#include "patch.h"
+
+
+/*  **ALL** IDs passed to these functions **MUST** be VALID.
+ *  in debugging mode, this is ensured with assertions.
+ *
+ *  patch_id:   0,1,2,3... < 
+ *  env_id:     MOD_SRC_EG, +0, +1, +2, +3...
+ *  lfo_id:     MOD_SRC_LFO, +0, +1, +2, +3...
+ *  slot:       0,1,2,3... < MAX_MOD_SLOTS
+ *
+ *  ALL setter functions return 0 on success (value set was in
+ *  range), and -1 on failure (value set was out of range) and
+ *  additionally set an error code (retrievable via pf_error_get).
+ */
+
+
+
+
+
+/* envelope setters */
+int patch_set_env_active  (int patch_id, int env_id, bool state);
+int patch_set_env_delay   (int patch_id, int env_id, float secs);
+int patch_set_env_attack  (int patch_id, int env_id, float secs);
+int patch_set_env_hold    (int patch_id, int env_id, float secs);
+int patch_set_env_decay   (int patch_id, int env_id, float secs);
+int patch_set_env_sustain (int patch_id, int env_id, float level);
+int patch_set_env_release (int patch_id, int env_id, float secs);
+int patch_set_env_key_amt (int patch_id, int env_id, float val);
+int patch_set_env_vel_amt (int patch_id, int env_id, float val);
+
+/* envelope getters */
+bool    patch_get_env_active  (int patch_id, int env_id);
+float   patch_get_env_delay   (int patch_id, int env_id);
+float   patch_get_env_attack  (int patch_id, int env_id);
+float   patch_get_env_hold    (int patch_id, int env_id);
+float   patch_get_env_decay   (int patch_id, int env_id);
+float   patch_get_env_sustain (int patch_id, int env_id);
+float   patch_get_env_release (int patch_id, int env_id);
+float   patch_get_env_key_amt (int patch_id, int env_id);
+float   patch_get_env_vel_amt (int patch_id, int env_id);
+
+/* lfo setters */
+int patch_set_lfo_active    (int patch_id, int lfo_id, bool state);
+int patch_set_lfo_attack    (int patch_id, int lfo_id, float secs);
+int patch_set_lfo_sync_beats(int patch_id, int lfo_id, float beats);
+int patch_set_lfo_delay     (int patch_id, int lfo_id, float secs);
+int patch_set_lfo_freq      (int patch_id, int lfo_id, float freq);
+int patch_set_lfo_positive  (int patch_id, int lfo_id, bool state);
+int patch_set_lfo_shape     (int patch_id, int lfo_id, LFOShape shape);
+int patch_set_lfo_sync      (int patch_id, int lfo_id, bool state);
+
+/* lfo getters */
+bool        patch_get_lfo_active    (int patch_id, int lfo_id);
+float       patch_get_lfo_attack    (int patch_id, int lfo_id);
+float       patch_get_lfo_sync_beats(int patch_id, int lfo_id);
+float       patch_get_lfo_delay     (int patch_id, int lfo_id);
+float       patch_get_lfo_freq      (int patch_id, int lfo_id);
+bool        patch_get_lfo_positive  (int patch_id, int lfo_id);
+LFOShape    patch_get_lfo_shape     (int patch_id, int lfo_id);
+bool        patch_get_lfo_sync      (int patch_id, int lfo_id);
+
+/* parameter setters */
+int patch_set_channel   (int patch_id, int channel);
+int patch_set_cut       (int patch_id, int cut);
+int patch_set_cut_by    (int patch_id, int cut_by);
+int patch_set_cutoff    (int patch_id, float freq);
+int patch_set_legato    (int patch_id, bool val);
+int patch_set_lower_note(int patch_id, int note);
+int patch_set_lower_vel (int patch_id, int vel);
+int patch_set_upper_vel (int patch_id, int vel);
+
+/* both of these return mark_id on success */
+int patch_set_mark_frame   (int patch_id, int mark_id, int frame);
+
+/* returns mark_id on sucessful setting, if another mark required
+    moving to accomodate the set mark, the id of the moved mark will
+    be set via also_changed which is otherwise -1.
+ */
+int patch_set_mark_frame_expand(int patch_id, int mark_id, int frame,
+                                                    int* also_changed);
+
+int patch_set_monophonic   (int id, bool val);
+int patch_set_name         (int id, const char* name);
+int patch_set_root_note    (int id, int note);
+int patch_set_panning      (int id, float pan);
+int patch_set_pitch        (int id, float pitch);
+int patch_set_pitch_steps  (int id, int steps);
+int patch_set_play_mode    (int id, PatchPlayMode mode);
+int patch_set_portamento   (int id, bool val);
+int patch_set_portamento_time(int id, float secs);
+int patch_set_resonance     (int id, float reso);
+
+int patch_set_upper_note   (int id, int note);
+int patch_set_amplitude    (int id, float vol);
+
+int patch_set_fade_samples (int id, int samples);
+int patch_set_xfade_samples(int id, int samples);
+
+/* parameter getters */
+int     patch_get_channel       (int id);
+int     patch_get_cut           (int id);
+int     patch_get_cut_by        (int id);
+float   patch_get_cutoff        (int id);
+int     patch_get_display_index (int id);
+int     patch_get_frames        (int id);
+bool    patch_get_legato        (int id);
+int     patch_get_lower_note    (int id);
+int     patch_get_lower_vel     (int id);
+int     patch_get_upper_vel     (int id);
+
+int     patch_get_mark_frame        (int patch_id, int mark_id);
+int     patch_get_mark_frame_range  (int patch_id, int mark_id,
+                                                   int* frame_min,
+                                                   int* frame_max);
+
+bool            patch_get_monophonic        (int id);
+char*           patch_get_name              (int id);
+int             patch_get_root_note         (int id);
+float           patch_get_panning           (int id);
+float           patch_get_pitch             (int id);
+int             patch_get_pitch_steps       (int id);
+PatchPlayMode   patch_get_play_mode         (int id);
+bool            patch_get_portamento        (int id);
+float           patch_get_portamento_time   (int id);
+
+
+float           patch_get_resonance         (int id);
+const float*    patch_get_sample            (int id);
+const char*     patch_get_sample_name       (int id);
+int             patch_get_upper_note        (int id);
+float           patch_get_amplitude         (int id);
+int             patch_get_fade_samples      (int id);
+int             patch_get_xfade_samples     (int id);
+int             patch_get_max_fade_samples  (int id);
+int             patch_get_max_xfade_samples (int id);
+
+
+/* returns 0 if non-raw sample loaded */
+int patch_get_raw_samplerate(int id);
+int patch_get_raw_channels(int id);
+int patch_get_raw_sndfile_format(int id);
+
+
+/* param */
+float   patch_param_get_value(int patch_id, PatchParamType);
+void    patch_param_set_value(int patch_id, PatchParamType, float  val);
+
+/* modulation setters */
+int patch_param_set_mod_src(int patch_id, PatchParamType,   int slot,
+                                                            int src_id);
+int patch_param_set_mod_amt(int patch_id, PatchParamType,   int slot,
+                                                            float amt);
+int patch_param_set_vel_amount(int id, PatchParamType param,float amt);
+int patch_param_set_key_amount(int id, PatchParamType param,float amt);
+
+/* modulation getters */
+int     patch_param_get_mod_src(int patch_id, PatchParamType,   int slot);
+float   patch_param_get_mod_amt(int patch_id, PatchParamType,   int slot);
+float   patch_param_get_vel_amount(int id, PatchParamType param);
+float   patch_param_get_key_amount(int id, PatchParamType param);
+
+
+/* PatchBool set/get */
+void    patch_bool_set_active(  int patch_id, PatchBoolType, bool);
+void    patch_bool_set_thresh(  int patch_id, PatchBoolType, float);
+void    patch_bool_set_mod_src( int patch_id, PatchBoolType, int mod_id);
+
+
+bool    patch_bool_get_active(  int patch_id, PatchBoolType);
+float   patch_bool_get_thresh(  int patch_id, PatchBoolType);
+int     patch_bool_get_mod_src( int patch_id, PatchBoolType);
+void    patch_bool_get_all(     int patch_id, PatchBoolType,
+                                              bool*  active,
+                                              float* thresh,
+                                              int*   mod_id);
+
+/* PatchFloat set/get */
+void    patch_float_set_value(  int patch_id, PatchFloatType, float);
+void    patch_float_set_mod_src(int patch_id, PatchFloatType, int mod_id);
+void    patch_float_set_mod_amt(int patch_id, PatchFloatType, float);
+
+float   patch_float_get_value(  int patch_id, PatchFloatType);
+int     patch_float_get_mod_src(int patch_id, PatchFloatType);
+float   patch_float_get_mod_amt(int patch_id, PatchFloatType);
+void    patch_float_get_all(    int patch_id, PatchFloatType,
+                                              float* value,
+                                              float* mod_amt,
+                                              int*   mod_id);
+
+/* lfo freq modulation setters */
+int patch_set_lfo_fm1_src(int patch_id, int lfo_id, int modsrc_id);
+int patch_set_lfo_fm2_src(int patch_id, int lfo_id, int modsrc_id);
+int patch_set_lfo_fm1_amt(int patch_id, int lfo_id, float amount);
+int patch_set_lfo_fm2_amt(int patch_id, int lfo_id, float amount);
+
+/* lfo amp modulation setters */
+int patch_set_lfo_am1_src(int patch_id, int lfo_id, int modsrc_id);
+int patch_set_lfo_am2_src(int patch_id, int lfo_id, int modsrc_id);
+int patch_set_lfo_am1_amt(int patch_id, int lfo_id, float amount);
+int patch_set_lfo_am2_amt(int patch_id, int lfo_id, float amount);
+
+/* lfo freq modulation getters */
+int     patch_get_lfo_fm1_src(int patch_id, int lfo_id);
+int     patch_get_lfo_fm2_src(int patch_id, int lfo_id);
+float   patch_get_lfo_fm1_amt(int patch_id, int lfo_id);
+float   patch_get_lfo_fm2_amt(int patch_id, int lfo_id);
+
+/* lfo amp modulation getters */
+int     patch_get_lfo_am1_src(int patch_id, int lfo_id);
+int     patch_get_lfo_am2_src(int patch_id, int lfo_id);
+float   patch_get_lfo_am1_amt(int patch_id, int lfo_id);
+float   patch_get_lfo_am2_amt(int patch_id, int lfo_id);
+
+
+
+#endif /* __PATCH_SET_AND_GET_H__ */
diff --git a/src/patch_util.c b/libpetrifoo/patch_util.c
similarity index 81%
rename from src/patch_util.c
rename to libpetrifoo/patch_util.c
index 4721917..5b43fd9 100644
--- a/src/patch_util.c
+++ b/libpetrifoo/patch_util.c
@@ -24,6 +24,7 @@
 
 #include "patch_util.h"
 
+#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
@@ -34,6 +35,7 @@
 #include "maths.h"
 #include "ticks.h"
 #include "patch.h"
+#include "pf_error.h"
 #include "sample.h"
 #include "adsr.h"
 #include "lfo.h"
@@ -48,10 +50,6 @@
 #include "patch_private/patch_macros.h"
 
 
-/*
-static int start_frame = 0;
-*/
-
 /**************************************************************************/
 /********************** PRIVATE GENERAL HELPER FUNCTIONS*******************/
 /**************************************************************************/
@@ -60,7 +58,7 @@ static int start_frame = 0;
 /*  inline definitions shared by patch and patch_util:
  *                          (see private/patch_data.h)
  */
-INLINE_ISOK_DEF
+INLINE_PATCHOK_DEF
 INLINE_PATCH_LOCK_DEF
 INLINE_PATCH_TRYLOCK_DEF
 INLINE_PATCH_UNLOCK_DEF
@@ -117,16 +115,17 @@ int patch_create(void)
     /* find unoccupied patch id */
     for (id = 0; patches[id] && patches[id]->active; ++id)
         if (id == PATCH_COUNT)
-            return PATCH_LIMIT;
+        {
+            pf_error(PF_ERR_PATCH_COUNT);
+            return -1;
+        }
 
     if (!(p = patch_new()))
     {
-        errmsg("Failed to create new patch\n");
-        return PATCH_ALLOC_FAIL;
+        pf_error(PF_ERR_PATCH_ALLOC);
+        return -1;
     }
 
-    debug("Creating patch %d [%p]\n", id, p);
-
     patches[id] = p;
     patch_lock(id);
     p->active = true;
@@ -149,13 +148,13 @@ int patch_create_default(void)
 
     patch_lock(id);
 
-    p->play_mode = PATCH_PLAY_LOOP | PATCH_PLAY_FORWARD;
+    p->play_mode = PATCH_PLAY_LOOP;
     p->fade_samples =  DEFAULT_FADE_SAMPLES;
     p->xfade_samples = DEFAULT_FADE_SAMPLES;
 
     /* adsr */
     eg1 = &p->env_params[0];
-    eg1->env_on  = true;
+    eg1->active  = true;
     eg1->attack  = 0.005;
     eg1->release = 0.375;
     eg1->key_amt = -0.99;
@@ -163,41 +162,41 @@ int patch_create_default(void)
     /* controllers... */
 
     /* pitch */
-    patch_set_mod_src(  id, PATCH_PARAM_PITCH, 0, MOD_SRC_PITCH_WHEEL);
-    patch_set_mod_amt(  id, PATCH_PARAM_PITCH, 0,
+    patch_param_set_mod_src(id, PATCH_PARAM_PITCH, 0, MOD_SRC_PITCH_WHEEL);
+    patch_param_set_mod_amt(id, PATCH_PARAM_PITCH, 0,
                                         6.0f / PATCH_MAX_PITCH_STEPS);
 
-    patch_set_mod_src(  id, PATCH_PARAM_PITCH, 1, MOD_SRC_VLFO);
-    patch_set_mod_amt(  id, PATCH_PARAM_PITCH, 1,
+    patch_param_set_mod_src(id, PATCH_PARAM_PITCH, 1, MOD_SRC_VLFO);
+    patch_param_set_mod_amt(id, PATCH_PARAM_PITCH, 1,
                                         2.0f / PATCH_MAX_PITCH_STEPS);
 
     /* AMPLITUDE EG_MOD_SLOT has full effect, no need to set amount */
-    patch_set_mod_src(  id, PATCH_PARAM_AMPLITUDE,  EG_MOD_SLOT,
+    patch_param_set_mod_src(id, PATCH_PARAM_AMPLITUDE,  EG_MOD_SLOT,
                                                         MOD_SRC_EG);
-    patch_set_mod_src(  id, PATCH_PARAM_AMPLITUDE, 0,
+    patch_param_set_mod_src(id, PATCH_PARAM_AMPLITUDE, 0,
                                 MOD_SRC_MIDI_CC | CC_CHANNEL_VOLUME);
-    patch_set_mod_amt(  id, PATCH_PARAM_AMPLITUDE, 0, 1.0f);
+    patch_param_set_mod_amt(id, PATCH_PARAM_AMPLITUDE, 0, 1.0f);
 
     /* pan */
-    patch_set_mod_src(  id, PATCH_PARAM_PANNING, 0,
+    patch_param_set_mod_src(id, PATCH_PARAM_PANNING, 0,
                                 MOD_SRC_MIDI_CC | CC_PAN);
-    patch_set_mod_amt(  id, PATCH_PARAM_PANNING, 0, 1.0f);
+    patch_param_set_mod_amt(id, PATCH_PARAM_PANNING, 0, 1.0f);
 
     /* filter cutoff */
     patch_param_set_value(  id, PATCH_PARAM_CUTOFF, 0.5f);
-    patch_set_mod_src(  id, PATCH_PARAM_CUTOFF, 0,
+    patch_param_set_mod_src(id, PATCH_PARAM_CUTOFF, 0,
                                 MOD_SRC_MIDI_CC | CC_SNDCTRL5_BRIGHTNESS);
-    patch_set_mod_amt(  id, PATCH_PARAM_CUTOFF, 0, 1.0f);
+    patch_param_set_mod_amt(id, PATCH_PARAM_CUTOFF, 0, 1.0f);
 
     /* filter resonance */
     patch_param_set_value(  id, PATCH_PARAM_RESONANCE, 0.0f);
-    patch_set_mod_src(  id, PATCH_PARAM_RESONANCE, 0,
+    patch_param_set_mod_src(id, PATCH_PARAM_RESONANCE, 0,
                                 MOD_SRC_MIDI_CC | CC_SNDCTRL2_TIMBRE);
-    patch_set_mod_amt(  id, PATCH_PARAM_RESONANCE, 0, 0.975f);
+    patch_param_set_mod_amt(id, PATCH_PARAM_RESONANCE, 0, 0.975f);
 
 
     /* setup VLFO0 to provide the pitch modulation */
-    patch_set_lfo_on(       id, MOD_SRC_VLFO, true);
+    patch_set_lfo_active(   id, MOD_SRC_VLFO, true);
     patch_set_lfo_freq(     id, MOD_SRC_VLFO, 9);
 
     /* and the MOD WHEEL to modulate VLFO0 amplitude */
@@ -210,6 +209,8 @@ int patch_create_default(void)
     patch_sample_load(id, "Default", 0, 0, 0);
     p->lower_note = 36;
     p->upper_note = 83;
+    p->lower_vel  = 0;
+    p->upper_vel  = 127;
 
     patch_set_name(id, "Default");
 
@@ -217,13 +218,12 @@ int patch_create_default(void)
 }
 
 
-int patch_destroy(int id)
+void patch_destroy(int id)
 {
     int index;
     Patch* p;
 
-    if (!isok(id))
-        return PATCH_ID_INVALID;
+    assert(patchok(id));
 
     debug ("Removing patch: %d\n", id);
 
@@ -250,8 +250,6 @@ int patch_destroy(int id)
             --patches[id]->display_index;
         }
     }
-
-    return 0;
 }
 
 
@@ -262,7 +260,8 @@ void patch_destroy_all(void)
     int id;
 
     for (id = 0; id < PATCH_COUNT; id++)
-        patch_destroy (id);
+        if (patches[id])
+            patch_destroy (id);
 
     return;
 }
@@ -285,7 +284,10 @@ int patch_dump(int **dump)
     /* allocate dump */
     *dump = malloc(sizeof(int) * count);
     if (*dump == NULL)
-        return PATCH_ALLOC_FAIL;
+    {
+        pf_error(PF_ERR_PATCH_DUMP_ALLOC);
+        return -1;
+    }
 
     /* place active patches into dump array */
     for (id = i = 0; id < PATCH_COUNT; id++)
@@ -321,8 +323,8 @@ int patch_dump(int **dump)
                 if (patches[(*dump)[k]]->channel != i)
                     continue;
 
-                if (patches[(*dump)[k]]->note <
-                    patches[(*dump)[j]]->note)
+                if (patches[(*dump)[k]]->root_note <
+                    patches[(*dump)[j]]->root_note)
                 {
                     tmp = (*dump)[j];
                     (*dump)[j] = (*dump)[k];
@@ -340,19 +342,13 @@ int patch_duplicate(int src_id)
     int i;
     int dest_id;
 
-    if (!isok(src_id))
-        return PATCH_ID_INVALID;
-
-    if (!patches[src_id]->active)
-        return PATCH_ID_INVALID;
+    assert(patchok(src_id));
+    assert(patches[src_id]->active);
 
     dest_id = patch_create();
 
     if (dest_id < 0)
-    {
-        debug("couldn't duplicate\n");
-        return dest_id;
-    }
+        return -1;
 
     debug("Creating patch (%d) from patch %s (%d).\n", dest_id,
            patches[src_id]->name, src_id);
@@ -387,11 +383,10 @@ int patch_duplicate(int src_id)
 int patch_flush (int id)
 {
     int i;
-     
-    if (!isok(id))
-        return PATCH_ID_INVALID;
 
-debug("flusing:%d\n",id);
+    assert(patchok(id));
+
+    debug("flusing:%d\n",id);
 
     patch_lock (id);
 
@@ -409,7 +404,7 @@ debug("flusing:%d\n",id);
 
     patch_unlock (id);
 
-debug("done\n");
+    debug("done\n");
     return 0;
 }
 
@@ -419,53 +414,11 @@ void patch_flush_all ( )
     int i;
 
     for (i = 0; i < PATCH_COUNT; i++)
-        patch_flush (i);
+        if (patches[i])
+            patch_flush (i);
 }
 
 
-/* returns error message associated with error code */
-const char *patch_strerror (int error)
-{
-    switch (error)
-    {
-    case PATCH_PARAM_INVALID:
-	return "patch parameter is invalid";
-	break;
-    case PATCH_ID_INVALID:
-	return "patch id is invalid";
-	break;
-    case PATCH_ALLOC_FAIL:
-	return "failed to allocate space for patch";
-	break;
-    case PATCH_NOTE_INVALID:
-	return "specified note is invalid";
-	break;
-    case PATCH_PAN_INVALID:
-	return "specified panning is invalid";
-	break;
-    case PATCH_CHANNEL_INVALID:
-	return "specified channel is invalid";
-	break;
-    case PATCH_VOL_INVALID:
-	return "specified amplitude is invalid";
-	break;
-    case PATCH_PLAY_MODE_INVALID:
-	return "specified patch play mode is invalid";
-	break;
-    case PATCH_LIMIT:
-	return "maximum patch count reached, can't create another";
-	break;
-    case PATCH_SAMPLE_INDEX_INVALID:
-	return "specified sample is invalid";
-	break;
-    default:
-	return "unknown error";
-	break;
-    }
-}
-
-
-
 /* loads a sample file for a patch */
 int patch_sample_load(int id, const char *name,
                                     int raw_samplerate,
@@ -473,18 +426,17 @@ int patch_sample_load(int id, const char *name,
                                     int sndfile_format)
 {
     int val;
+    double ratio = (patch_samplerate == 44100)
+                            ? 1
+                            : (patch_samplerate / 44100.0f);
+    int frames;
+
     bool defsample = (strcmp(name, "Default") == 0);
 
-    if (!isok (id))
-        return PATCH_ID_INVALID;
+    assert(patchok(id));
+    assert(name != NULL);
 
-    if (name == NULL)
-    {
-        debug ("Refusing to load null sample for patch %d\n", id);
-        return PATCH_PARAM_INVALID;
-    }
-
-    debug ("Loading sample %s for patch %d\n", name, id);
+    debug("Loading sample %s for patch %d\n", name, id);
     patch_flush (id);
 
     /* we lock *after* we call patch_flush because patch_flush does
@@ -500,22 +452,27 @@ int patch_sample_load(int id, const char *name,
                                             raw_channels,
                                             sndfile_format);
 
-    patches[id]->sample_stop = patches[id]->sample->frames - 1;
+    if (val < 0)
+        frames = 0;
+    else
+        frames = patches[id]->sample->frames - 1;
+
+    patches[id]->sample_stop = frames;
 
     patches[id]->play_start = 0;
     patches[id]->play_stop = patches[id]->sample_stop;
 
     if (defsample)
     {
-        patches[id]->loop_start = 296;
-        patches[id]->loop_stop = 5203;
-        patches[id]->fade_samples = 100;
+        patches[id]->loop_start = 296 * ratio;
+        patches[id]->loop_stop = 5203 * ratio;
+        patches[id]->fade_samples = 100 * ratio;
         patches[id]->xfade_samples = 0;
     }
     else
     {
-        patches[id]->fade_samples = 100;
-        patches[id]->xfade_samples = 100;
+        patches[id]->fade_samples = (frames / 2 > 100) ? 100 * ratio : 0;
+        patches[id]->xfade_samples = (frames / 2 > 100) ? 100 * ratio : 0;
         patches[id]->loop_start = patches[id]->xfade_samples;
         patches[id]->loop_stop = patches[id]->sample_stop -
                                     patches[id]->xfade_samples;
@@ -535,8 +492,8 @@ int patch_sample_load_from(int dest_id, int src_id)
     const char* name;
     bool defsample;
 
-    if (!isok(dest_id) || !isok(src_id))
-        return PATCH_ID_INVALID;
+    assert(patchok(dest_id));
+    assert(patchok(src_id));
 
     name = patches[src_id]->sample->filename;
     defsample = (strcmp(name, "Default") == 0);
@@ -576,9 +533,7 @@ int patch_sample_load_from(int dest_id, int src_id)
 
 const Sample* patch_sample_data(int id)
 {
-    if (!isok(id))
-        return 0;
-
+    assert(patchok(id));
     return patches[id]->sample;
 }
 
@@ -586,9 +541,8 @@ const Sample* patch_sample_data(int id)
 /* unloads a patch's sample */
 void patch_sample_unload (int id)
 {
-    if (!isok(id))
-	return;
-     
+    assert(patchok(id));
+
     debug ("Unloading sample for patch %d\n", id);
     patch_lock (id);
 
@@ -661,6 +615,13 @@ void patch_set_samplerate (int rate)
     }
 }
 
+
+int patch_get_samplerate(void)
+{
+    return patch_samplerate;
+}
+
+
 /* destructor */
 void patch_shutdown ( )
 {
diff --git a/src/patch_util.h b/libpetrifoo/patch_util.h
similarity index 95%
rename from src/patch_util.h
rename to libpetrifoo/patch_util.h
index 6fbe3de..9ef82ac 100644
--- a/src/patch_util.h
+++ b/libpetrifoo/patch_util.h
@@ -37,7 +37,7 @@ enum { USER_PATCH, DEFAULT_PATCH };
 int         patch_create          (void);
 int         patch_create_default  (void);
 
-int         patch_destroy         (int id);
+void        patch_destroy         (int id);
 void        patch_destroy_all     (void);
 
 int         patch_count           (void);
@@ -57,8 +57,12 @@ int         patch_sample_load_from(int dest_id, int src_id);
 const Sample* patch_sample_data(int id);
 
 void        patch_sample_unload   (int id);
+
+
 void        patch_set_buffersize  (int nframes);
 void        patch_set_samplerate  (int rate);
+int         patch_get_samplerate  (void);
+
 void        patch_shutdown        (void);
 void        patch_sync            (float bpm);
 int         patch_verify          (int id);
diff --git a/src/petri-foo.h b/libpetrifoo/petri-foo.h
similarity index 53%
rename from src/petri-foo.h
rename to libpetrifoo/petri-foo.h
index 29eafdf..bf6975f 100644
--- a/src/petri-foo.h
+++ b/libpetrifoo/petri-foo.h
@@ -25,26 +25,47 @@
 #ifndef __SPECIMEN_H__
 #define __SPECIMEN_H__
 
-#include <config.h>
+#include "config.h"
 #include <stdio.h>
 #include <signal.h>
 
+
+#define DEFAULT_AMPLITUDE 0.7
+
+
+#define CHARBUFSIZE 256
+
+
 #ifndef DEBUG
-# define DEBUG 0
+#define DEBUG 0
 #endif
 
-enum
-{
-    FUBAR = -69
-};
-
-#define DEFAULT_AMPLITUDE 0.7 /* default amplitude stuff is set to, from 0 to 1 */
+/*
+#define errmsg(fmt, ...)                                    \
+{                                                           \
+    msg_log(MSG_CRITICAL,                                   \
+        "%20s:%5d\t%30s" fmt,                               \
+        __FILE__, __LINE__, __FUNCTION__ , ## __VA_ARGS__); \
+}
 
-#ifndef PIXMAPSDIR
-# define PIXMAPSDIR INSTALLDIR"/petri-foo/pixmaps/"
+#if DEBUG
+#define debug(fmt, ...)                                     \
+{                                                           \
+    msg_log(MSG_DEBUG,                                      \
+        "%20s:%5d\t%30s" fmt,                               \
+        __FILE__, __LINE__, __FUNCTION__ , ## __VA_ARGS__); \
+}
+#else
+#define debug(...)
 #endif
+*/
 
-#define errmsg(...) {fprintf(stderr, "%20s:%5d\t%30s", __FILE__, __LINE__, __FUNCTION__); fprintf(stderr, ": "); fprintf(stderr, __VA_ARGS__);}
+#define errmsg(...) \
+{                   \
+    fprintf(stderr, "%20s:%5d\t%30s", __FILE__, __LINE__, __FUNCTION__); \
+    fprintf(stderr, ": ");          \
+    fprintf(stderr, __VA_ARGS__);   \
+}
 
 #if DEBUG
 # define debug(...) errmsg(__VA_ARGS__)
@@ -52,6 +73,8 @@ enum
 # define debug(...)
 #endif
 
+
 typedef sig_atomic_t Atomic;
 
+
 #endif /* __SPECIMEN_H__ */
diff --git a/libpetrifoo/pf_error.c b/libpetrifoo/pf_error.c
new file mode 100644
index 0000000..8472e17
--- /dev/null
+++ b/libpetrifoo/pf_error.c
@@ -0,0 +1,96 @@
+#include "pf_error.h"
+
+#include <assert.h>
+#include "petri-foo.h"
+
+static int last_error_no = PF_ERR_INVALID_ERROR;
+
+
+void pf_error(int pf_error_no)
+{
+    assert (last_error_no == PF_ERR_INVALID_ERROR);
+    last_error_no = pf_error_no;
+}
+
+
+int pf_error_get(void)
+{
+    int errno = last_error_no;
+    last_error_no = PF_ERR_INVALID_ERROR;
+}
+
+const char* pf_error_str(int pf_error_no)
+{
+    switch(pf_error_no)
+    {
+    /* JACK errors */
+    case PF_ERR_JACK_OPEN_CLIENT:
+        return "JACK failed to open client";
+    case PF_ERR_JACK_ACTIVATE:
+        return "JACK failed to activate client";
+    case PF_ERR_JACK_SESSION_CB:
+        return "JACK failed to set session callback";
+    case PF_ERR_JACK_BUF_ALLOC:
+        return "JACK failed to allocate buffer";
+    case PF_ERR_JACK_BUF_SIZE_CHANGE:
+        return "JACK failed buffer size change";
+
+    /* Patch errors */
+    case PF_ERR_PATCH_ID:
+        return "Invalid patch ID";
+    case PF_ERR_PATCH_COUNT:
+        return "Maximum patch count exceeded";
+    case PF_ERR_PATCH_ALLOC:
+        return "Patch allocation failed";
+    case PF_ERR_PATCH_DUMP_ALLOC:
+        return "Patch dump allocation failed";
+    case PF_ERR_PATCH_PARAM_ID:
+        return "Invalid param ID";
+    case PF_ERR_PATCH_BOOL_ID:
+        return "Invalid bool ID";
+    case PF_ERR_PATCH_FLOAT_ID:
+        return "Invalid float ID";
+    case PF_ERR_PATCH_ENV_ID:
+        return "Invalid ADSR ID";
+    case PF_ERR_PATCH_LFO_ID:
+        return "Invalid LFO ID";
+    case PF_ERR_PATCH_MOD_SRC_ID:
+        return "Invalid modulation-source ID";
+    case PF_ERR_PATCH_MOD_SLOT:
+        return "Invalid modulation-slot ID";
+    case PF_ERR_PATCH_CHANNEL:
+        return "Invalid channel number";
+    case PF_ERR_PATCH_NOTE:
+        return "Invalid note";
+    case PF_ERR_PATCH_PARAM_VALUE:
+        return "Invalud parameter value";
+
+    /* Sample errors */
+    case PF_ERR_SAMPLE_ALLOC:
+        return "Failed to allocate sample";
+    case PF_ERR_SAMPLE_DEFAULT_ALLOC:
+        return "Failed to allocate default sample";
+    case PF_ERR_SAMPLE_ALLOC_COPY:
+        return "Failed to allocate duplicate sample";
+    case PF_ERR_SAMPLE_MAX_FRAMES:
+        return "Sample is too long";
+    case PF_ERR_SAMPLE_RESAMPLE_MAX_FRAMES:
+        return "Sample (after resampling) is too long";
+    case PF_ERR_SAMPLE_RESAMPLE_ALLOC:
+        return "Failed to allocate resampling data";
+    case PF_ERR_SAMPLE_CHANNEL_COUNT:
+        return "Sample contains too many channels";
+    case PF_ERR_SAMPLE_CHANNEL_ALLOC:
+        return "Allocation for mono to stereo conversion failed";
+    case PF_ERR_SAMPLE_SNDFILE_FORMAT:
+        return "Sndfile format error";
+    case PF_ERR_SAMPLE_SNDFILE_OPEN:
+        return "Sndfile could not open sample";
+    case PF_ERR_SAMPLE_SNDFILE_READ:
+        return "Sndfile could not read sample";
+    case PF_ERR_SAMPLE_SRC_SIMPLE:
+        return "Secret Rabbit Code resample failed";
+    default:
+        return "uncategorized error";
+    }
+}
diff --git a/libpetrifoo/pf_error.h b/libpetrifoo/pf_error.h
new file mode 100644
index 0000000..efc3642
--- /dev/null
+++ b/libpetrifoo/pf_error.h
@@ -0,0 +1,57 @@
+#ifndef PF_ERROR_H
+#define PF_ERROR_H
+
+enum
+{
+    PF_ERR_INVALID_ERROR = -1,
+
+    /* JACK errors */
+    PF_ERR_JACK_OPEN_CLIENT,
+    PF_ERR_JACK_ACTIVATE,
+    PF_ERR_JACK_SESSION_CB,
+    PF_ERR_JACK_BUF_ALLOC,
+    PF_ERR_JACK_BUF_SIZE_CHANGE,
+
+    /* Patch errors */
+    /*  (note: ID errors are only introducible via dish_file_read) */
+    PF_ERR_PATCH_ID,
+    PF_ERR_PATCH_COUNT,
+    PF_ERR_PATCH_ALLOC,
+    PF_ERR_PATCH_DUMP_ALLOC,
+    PF_ERR_PATCH_PARAM_ID,
+    PF_ERR_PATCH_BOOL_ID,
+    PF_ERR_PATCH_FLOAT_ID,
+    PF_ERR_PATCH_ENV_ID,
+    PF_ERR_PATCH_LFO_ID,
+    PF_ERR_PATCH_MOD_SRC_ID,
+    PF_ERR_PATCH_MOD_SLOT,
+    PF_ERR_PATCH_CHANNEL,
+    PF_ERR_PATCH_NOTE,
+
+    PF_ERR_PATCH_VALUE_NEGATIVE,
+    PF_ERR_PATCH_VALUE_LEVEL,
+
+    PF_ERR_PATCH_PARAM_VALUE,
+
+    /* Sample errors */
+    PF_ERR_SAMPLE_ALLOC,
+    PF_ERR_SAMPLE_DEFAULT_ALLOC,
+    PF_ERR_SAMPLE_ALLOC_COPY,
+    PF_ERR_SAMPLE_MAX_FRAMES,
+    PF_ERR_SAMPLE_RESAMPLE_MAX_FRAMES,
+    PF_ERR_SAMPLE_RESAMPLE_ALLOC,
+    PF_ERR_SAMPLE_CHANNEL_COUNT,
+    PF_ERR_SAMPLE_CHANNEL_ALLOC,
+    PF_ERR_SAMPLE_SNDFILE_FORMAT,
+    PF_ERR_SAMPLE_SNDFILE_OPEN,
+    PF_ERR_SAMPLE_SNDFILE_READ,
+    PF_ERR_SAMPLE_SRC_SIMPLE,
+};
+
+
+void        pf_error(int pf_error_no);
+int         pf_error_get(void);
+const char* pf_error_str(int pf_error_no);
+
+
+#endif
diff --git a/src/sample.c b/libpetrifoo/sample.c
similarity index 62%
rename from src/sample.c
rename to libpetrifoo/sample.c
index 8d54a25..001e375 100644
--- a/src/sample.c
+++ b/libpetrifoo/sample.c
@@ -27,10 +27,12 @@
 #include <string.h>
 #include <sndfile.h>
 #include <samplerate.h>
+
+#include "lfo.h"
 #include "petri-foo.h"
+#include "pf_error.h"
+#include "names.h"
 #include "sample.h"
-#include "lfo.h"
-
 
 #include <stdbool.h>
 
@@ -40,7 +42,10 @@ Sample* sample_new(void)
     Sample* sample = malloc(sizeof(*sample));
 
     if (!sample)
+    {
+        pf_error(PF_ERR_SAMPLE_ALLOC);
         return 0;
+    }
 
     sample->sp = 0;
     sample->frames = 0;
@@ -55,6 +60,7 @@ Sample* sample_new(void)
     return sample;
 }
 
+
 void sample_free (Sample* sample)
 {
     free(sample->filename);
@@ -85,12 +91,15 @@ int sample_default(Sample* sample, int rate)
     LFO*        lfo;
     LFOParams   lfopar;
     int         i;
+    double      v;
 
     float const*    lfo_out;
 
+    debug("Creating default sample\n");
+
     if (!(tmp = malloc(frames * 2 * sizeof(*tmp))))
     {
-        errmsg("Unable to allocate space for (default) samples!\n");
+        pf_error(PF_ERR_SAMPLE_DEFAULT_ALLOC);
         return -1;
     }
 
@@ -107,13 +116,11 @@ int sample_default(Sample* sample, int rate)
     for (i = 0; i < frames; ++i)
     {
         lfo_tick(lfo);
-        *tmp++ = *lfo_out;
-        *tmp++ = *lfo_out;
 
-        if (*lfo_out < -1.0 || *lfo_out > 1.0)
-        {
-            debug("lfo output %1.3f clips -1.0 || 1.0\n", *lfo_out);
-        }
+        v = *lfo_out * 0.9;
+
+        *tmp++ = v;
+        *tmp++ = v;
     }
 
     lfo_free(lfo);
@@ -128,48 +135,44 @@ int sample_default(Sample* sample, int rate)
 static float* resample(float* samples, int rate, SF_INFO* sfinfo)
 {
     double ratio;
-    int frames;
+    int err;
+    SRC_DATA src;
+    float* tmp;
 
     ratio = rate / (sfinfo->samplerate * 1.0);
-    frames = (int)sfinfo->frames;
 
-    debug("Resampling...\n");
-
-    int err;
-    SRC_DATA src;
-    float* tmp = malloc(sizeof(float) * sfinfo->frames
-                                      * sfinfo->channels
-                                      * ratio);
-    if (!tmp)
-    {
-        errmsg ("Out of memory for resampling\n");
-        return 0;
-    }
+    debug("Resampling from %d to %d\n", rate, sfinfo->samplerate);
 
     src.src_ratio = ratio;
     src.data_in = samples;
-    src.data_out = tmp;
     src.input_frames = sfinfo->frames;
     src.output_frames = sfinfo->frames * ratio;
 
-    frames = (int)src.output_frames;
+    if (src.output_frames >= MAX_SAMPLE_FRAMES)
+    {
+        pf_error(PF_ERR_SAMPLE_RESAMPLE_MAX_FRAMES);
+        return 0;
+    }
 
-    if (frames != src.output_frames)
+    tmp = malloc(sizeof(float)
+                * sfinfo->frames * sfinfo->channels * ratio);
+    if (!tmp)
     {
-        errmsg("resampled sample would be too long\n");
-        free(tmp);
+        pf_error(PF_ERR_SAMPLE_RESAMPLE_ALLOC);
         return 0;
     }
 
+    src.data_out = tmp;
+
     err = src_simple(&src, SRC_SINC_BEST_QUALITY, sfinfo->channels);
     if (err)
     {
-        errmsg("Failed to resample (%s)\n", src_strerror(err));
+        pf_error(PF_ERR_SAMPLE_SRC_SIMPLE);
         free(tmp);
         return 0;
     }
 
-    sfinfo->frames = frames;
+    sfinfo->frames = src.output_frames;
 
     return tmp;
 }
@@ -177,14 +180,14 @@ static float* resample(float* samples, int rate, SF_INFO* sfinfo)
 
 static float* mono_to_stereo(float* samples, SF_INFO* sfinfo)
 {
-    debug ("Converting mono to stereo...\n");
+    debug("Converting mono to stereo...\n");
 
     int i;
     float* tmp = malloc(sizeof(float) * sfinfo->frames * 2);
 
     if (!tmp)
     {
-        errmsg ("Out of memory for mono to stereo conversion.\n");
+        pf_error(PF_ERR_SAMPLE_CHANNEL_ALLOC);
         return 0;
     }
 
@@ -198,17 +201,16 @@ static float* mono_to_stereo(float* samples, SF_INFO* sfinfo)
 static float* read_audio(SNDFILE* sfp, SF_INFO* sfinfo)
 {
     float* tmp;
-    int frames = (int)sfinfo->frames;
 
-    if (frames != sfinfo->frames)
+    if (sfinfo->frames >= MAX_SAMPLE_FRAMES)
     {
-        errmsg("sample is too long\n");
+        pf_error(PF_ERR_SAMPLE_MAX_FRAMES);
         return 0;
     }
 
     if (sfinfo->channels > 2)
     {
-        errmsg ("Data can't have more than 2 channels\n");
+        pf_error(PF_ERR_SAMPLE_CHANNEL_COUNT);
         sf_close (sfp);
         return 0;
     }
@@ -216,7 +218,7 @@ static float* read_audio(SNDFILE* sfp, SF_INFO* sfinfo)
     /* set aside space for samples */
     if (!(tmp = malloc(sfinfo->frames * sfinfo->channels * sizeof(*tmp))))
     {
-        errmsg ("Unable to allocate space for samples!\n");
+        pf_error(PF_ERR_SAMPLE_ALLOC);
         sf_close (sfp);
         return 0;
     }
@@ -224,44 +226,115 @@ static float* read_audio(SNDFILE* sfp, SF_INFO* sfinfo)
     /* load sample file into memory */
     if (sf_readf_float(sfp, tmp, sfinfo->frames) != sfinfo->frames)
     {
-        errmsg("libsndfile had problems reading file, aborting\n");
+        pf_error(PF_ERR_SAMPLE_SNDFILE_READ);
         free(tmp);
         return 0;
     }
 
-    debug ("Read %d frames into memory.\n", (int) sfinfo->frames);
+    debug("Read %d frames into memory.\n", (int) sfinfo->frames);
 
     return tmp;
 }
 
 
-int sample_load_file(Sample* sample, const char* name,
-                                        int rate,
+static SNDFILE* open_sample(SF_INFO* sfinfo, const char* name,
                                         int raw_samplerate,
                                         int raw_channels,
                                         int sndfile_format)
 {
-    SNDFILE* sfp;
-    SF_INFO sfinfo = { 0, 0, 0, 0, 0, 0 };
-    float* tmp;
+    SNDFILE* sfp = NULL;
+
     bool raw = (raw_samplerate || raw_channels || sndfile_format);
 
+    sfinfo->frames = 0;
+    sfinfo->samplerate = 0;
+    sfinfo->channels = 0;
+    sfinfo->format = 0;
+    sfinfo->sections = 0;
+    sfinfo->seekable = 0;
+
     if (raw)
     {
-        sfinfo.samplerate = raw_samplerate;
-        sfinfo.channels = raw_channels;
-        sfinfo.format = sndfile_format;
+        id_name* idnames = names_sample_raw_format_get();
+        char* fmt_name = 0;
+        int i;
+
+        for (i = 0; idnames[i].name != 0; ++i)
+            if (idnames[i].id == sndfile_format)
+                fmt_name = idnames[i].name;
 
-        if (!sf_format_check(&sfinfo))
+        sfinfo->samplerate = raw_samplerate;
+        sfinfo->channels =   raw_channels;
+        sfinfo->format =     sndfile_format;
+
+        debug("Reading raw sample %s as %d %s %s\n",
+                                name, raw_samplerate,
+                                (raw_channels == 2)? "stereo" : "mono",
+                                fmt_name);
+
+        if (!sf_format_check(sfinfo))
         {
-            debug("LIBSNDFILE found error in format. aborting.\n");
-            return -1;
+            pf_error(PF_ERR_SAMPLE_SNDFILE_FORMAT);
+            return 0;
         }
     }
+    else
+        debug("Reading sample %s\n", name);
 
-    if ((sfp = sf_open(name, SFM_READ, &sfinfo)) == NULL)
+    if ((sfp = sf_open(name, SFM_READ, sfinfo)) == NULL)
+    {
+        pf_error(PF_ERR_SAMPLE_SNDFILE_OPEN);
+        return 0;
+    }
+
+    return sfp;
+}
+
+
+int sample_get_resampled_size(const char* name, int rate,
+                                        int raw_samplerate,
+                                        int raw_channels,
+                                        int sndfile_format)
+{
+    SF_INFO sfinfo;
+    SNDFILE* sfp;
+    sf_count_t frames;
+
+    if (!(sfp = open_sample(&sfinfo, name,  raw_samplerate,
+                                            raw_channels,
+                                            sndfile_format)))
+    {
+        return -1;
+    }
+
+    sf_close(sfp);
+
+    if (sfinfo.samplerate == rate)
+    {
+        double ratio = rate / (sfinfo.samplerate * 1.0);
+        frames = sfinfo.frames * ratio;
+    }
+    else
+        frames = sfinfo.frames;
+
+    return frames < MAX_SAMPLE_FRAMES ? frames : 0;
+}
+
+
+int sample_load_file(Sample* sample, const char* name,
+                                        int rate,
+                                        int raw_samplerate,
+                                        int raw_channels,
+                                        int sndfile_format)
+{
+    float* tmp;
+    SF_INFO sfinfo;
+    SNDFILE* sfp;
+
+    if (!(sfp = open_sample(&sfinfo, name,  raw_samplerate,
+                                            raw_channels,
+                                            sndfile_format)))
     {
-        debug ("libsndfile doesn't like %s\n", name);
         return -1;
     }
 
@@ -270,6 +343,19 @@ int sample_load_file(Sample* sample, const char* name,
 
     sf_close(sfp);
 
+    if (raw_samplerate || raw_channels || sndfile_format)
+    {
+        sample->raw_samplerate = raw_samplerate;
+        sample->raw_channels =   raw_channels;
+        sample->sndfile_format = sndfile_format;
+    }
+    else
+    {
+        sample->raw_samplerate = 0;
+        sample->raw_channels = 0;
+        sample->sndfile_format = 0;
+    }
+
     if (sfinfo.samplerate != rate)
     {
         float* tmp2 = resample(tmp, rate, &sfinfo);
@@ -277,7 +363,6 @@ int sample_load_file(Sample* sample, const char* name,
         if (!tmp2)
         {
             free(tmp);
-            debug("failed to resample file\n");
             return -1;
         }
 
@@ -292,7 +377,6 @@ int sample_load_file(Sample* sample, const char* name,
         if (!tmp2)
         {
             free(tmp);
-            debug("failed to convert mono to stereo\n");
             return -1;
         }
 
@@ -300,35 +384,16 @@ int sample_load_file(Sample* sample, const char* name,
         tmp = tmp2;
     }
 
-debug("freeing old sample data\n");
-
     free(sample->sp);
     free(sample->filename);
 
-debug("setting new sample data\n");
-
     sample->filename = strdup(name);
 
     sample->sp = tmp;
     sample->frames = sfinfo.frames;
 
-    if (raw)
-    {
-        sample->raw_samplerate = raw_samplerate;
-        sample->raw_channels = raw_channels;
-        sample->sndfile_format = sndfile_format;
-    }
-    else
-    {
-        sample->raw_samplerate = 0;
-        sample->raw_channels = 0;
-        sample->sndfile_format = 0;
-    }
-
     sample->default_sample = false;
 
-debug("sample loaded\n");
-
     return 0;
 }
 
@@ -353,7 +418,7 @@ int sample_deep_copy(Sample* dest, const Sample* src)
 
     if (!dest->sp)
     {
-        debug("Failed to allocate memory for sample data copy\n");
+        pf_error(PF_ERR_SAMPLE_ALLOC_COPY);
         return -1;
     }
 
diff --git a/src/sample.h b/libpetrifoo/sample.h
similarity index 83%
rename from src/sample.h
rename to libpetrifoo/sample.h
index 722d76e..da88045 100644
--- a/src/sample.h
+++ b/libpetrifoo/sample.h
@@ -27,6 +27,10 @@
 
 
 #include <stdbool.h>
+#include <stdint.h>
+
+
+enum { MAX_SAMPLE_FRAMES = INT32_MAX };
 
 
 typedef struct _RAW_FORMAT
@@ -44,11 +48,8 @@ struct _Sample
 {
     /* Public */
     float* sp;          /* samples pointer */
-    int frames;         /* number of frames (not samples). 
-                         * samples whose length exceeds int on the
-                         * system they're to run on are disallowed.
-                         * (on 64bit, this still is OTT long).
-                         */
+    int frames;         /* number of frames (not samples)
+                           (frames < MAX_SAMPLE_FRAMES) == true */
 
     int raw_samplerate; /* if the sample was a regular sound file ie */
     int raw_channels;   /* with a header, then these fields will be  */
@@ -70,6 +71,12 @@ void        sample_shallow_copy(Sample* dest, const Sample* src);
 int         sample_deep_copy(Sample* dest, const Sample* src);
 
 
+int         sample_get_resampled_size(const char* name, int rate,
+    /* zero for non-raw data */         int raw_samplerate,
+    /* zero for non-raw data */         int raw_channels,
+    /* zero for non-raw data */         int sndfile_format);
+
+
 int         sample_load_file(Sample*, const char* name, int rate,
     /* zero for non-raw data */         int raw_samplerate,
     /* zero for non-raw data */         int raw_channels,
diff --git a/src/sync.c b/libpetrifoo/sync.c
similarity index 100%
rename from src/sync.c
rename to libpetrifoo/sync.c
diff --git a/src/sync.h b/libpetrifoo/sync.h
similarity index 100%
rename from src/sync.h
rename to libpetrifoo/sync.h
diff --git a/src/ticks.c b/libpetrifoo/ticks.c
similarity index 100%
rename from src/ticks.c
rename to libpetrifoo/ticks.c
diff --git a/src/ticks.h b/libpetrifoo/ticks.h
similarity index 100%
rename from src/ticks.h
rename to libpetrifoo/ticks.h
diff --git a/libpetrifui/CMakeLists.txt b/libpetrifui/CMakeLists.txt
new file mode 100644
index 0000000..84493d5
--- /dev/null
+++ b/libpetrifui/CMakeLists.txt
@@ -0,0 +1,22 @@
+
+file (GLOB LIBPETRIFUI_SOURCES *.c)
+
+include_directories (
+    ${Petri-Foo_SOURCE_DIR}/libpetrifoo
+    ${LIBXML2_INCLUDE_DIRS}
+    )
+
+link_directories (
+    ${LIBXML2_LIBRARY_DIRS}
+    )
+
+add_definitions (
+    ${LIBXML2_CFLAGS_OTHER}
+    )
+
+add_library( petrifui ${LIBPETRIFUI_SOURCES})
+
+target_link_Libraries(  petrifui
+                        petrifoo
+                        ${LIBXML2_LIBRARIES}
+                    )
diff --git a/src/dish_file.c b/libpetrifui/dish_file.c
similarity index 69%
rename from src/dish_file.c
rename to libpetrifui/dish_file.c
index 0a9fd15..08dcb3d 100644
--- a/src/dish_file.c
+++ b/libpetrifui/dish_file.c
@@ -29,16 +29,18 @@
 
 #include "mixer.h"
 #include "mod_src.h"
-#include "petri-foo.h"
+#include "msg_log.h"
 #include "patch.h"
 #include "patch_util.h"
 #include "patch_set_and_get.h"
+#include "petri-foo.h"
+#include "pf_error.h"
 #include "sample.h"
 
 
-#define BUFSIZE 256
 
 static const char* dish_file_ext = ".petri-foo";
+static int dish_file_samplerate = 0;
 
 
 const char* dish_file_extension(void)
@@ -76,20 +78,20 @@ static int dish_file_write_sample_raw(xmlNodePtr nodeparent, int patch_id)
 {
     xmlNodePtr node;
     const Sample* s = patch_sample_data(patch_id);
-    char buf[BUFSIZE];
+    char buf[CHARBUFSIZE];
 
     if (!(s->raw_samplerate || s->raw_channels || s->sndfile_format))
         return 0;
 
     node = xmlNewTextChild(nodeparent, NULL, BAD_CAST "Raw", NULL);
 
-    snprintf(buf, BUFSIZE, "%d", s->raw_samplerate);
+    snprintf(buf, CHARBUFSIZE, "%d", s->raw_samplerate);
     xmlNewProp(node, BAD_CAST "samplerate", BAD_CAST buf);
 
-    snprintf(buf, BUFSIZE, "%d", s->raw_channels);
+    snprintf(buf, CHARBUFSIZE, "%d", s->raw_channels);
     xmlNewProp(node, BAD_CAST "channels", BAD_CAST buf);
 
-    snprintf(buf, BUFSIZE, "%d", s->sndfile_format);
+    snprintf(buf, CHARBUFSIZE, "%d", s->sndfile_format);
     xmlNewProp(node, BAD_CAST "sndfile_format", BAD_CAST buf);
 
     return 0;
@@ -102,7 +104,7 @@ dish_file_write_param(xmlNodePtr nodeparent, int patch_id,
 {
     xmlNodePtr  node1;
     xmlNodePtr  node2;
-    char buf[BUFSIZE];
+    char buf[CHARBUFSIZE];
     const char** param_names;
 
     const char* prop1 = 0;
@@ -111,22 +113,17 @@ dish_file_write_param(xmlNodePtr nodeparent, int patch_id,
     float   val1;
     float   val2;
     float   vel_amt;
+    float   key_trk;
     int     modsrc;
     float   modamt;
-    float   velsens;
-    float   keytrack;
 
     int     last_mod_slot = MAX_MOD_SLOTS;
     int     i;
 
-    if (patch_param_get_value(patch_id, param, &val1)
-                                                == PATCH_PARAM_INVALID)
-        return -1;
+    val1 = patch_param_get_value(patch_id, param);
 
     param_names = names_params_get();
 
-    patch_get_vel_amount(patch_id, param, &vel_amt);
-
     switch(param)
     {
     case PATCH_PARAM_AMPLITUDE: prop1 = "level";    prop2 = 0;
@@ -144,42 +141,42 @@ dish_file_write_param(xmlNodePtr nodeparent, int patch_id,
     node1 = xmlNewTextChild(nodeparent, NULL,
                             BAD_CAST param_names[param], NULL);
 
-    snprintf(buf, BUFSIZE, "%f", val1);
+    snprintf(buf, CHARBUFSIZE, "%f", val1);
     xmlNewProp(node1, BAD_CAST prop1, BAD_CAST buf);
 
     if (prop2)
     {
-        snprintf(buf, BUFSIZE, "%f", val2);
+        snprintf(buf, CHARBUFSIZE, "%f", val2);
         xmlNewProp(node1, BAD_CAST prop2, BAD_CAST buf);
     }
 
     /* velocity sensing */
-    patch_get_vel_amount(patch_id, param, &velsens);
-    snprintf(buf, BUFSIZE, "%f", velsens);
+    vel_amt = patch_param_get_vel_amount(patch_id, param);
+    snprintf(buf, CHARBUFSIZE, "%f", vel_amt);
     xmlNewProp(node1, BAD_CAST "velocity_sensing", BAD_CAST buf);
 
     /* keyboard tracking */
-    patch_get_key_amount(patch_id, param, &keytrack);
-    snprintf(buf, BUFSIZE, "%f", keytrack);
+    key_trk = patch_param_get_key_amount(patch_id, param);
+    snprintf(buf, CHARBUFSIZE, "%f", key_trk);
     xmlNewProp(node1, BAD_CAST "key_tracking", BAD_CAST buf);
 
     for (i = 0; i < last_mod_slot; ++i)
     {
-        snprintf(buf, BUFSIZE, "Mod%d", i + 1);
+        snprintf(buf, CHARBUFSIZE, "Mod%d", i + 1);
 
-        patch_get_mod_src(patch_id, param, i, &modsrc);
-        patch_get_mod_amt(patch_id, param, i, &modamt);
+        modsrc = patch_param_get_mod_src(patch_id, param, i);
+        modamt = patch_param_get_mod_amt(patch_id, param, i);
 
         node2 = xmlNewTextChild(node1, NULL, BAD_CAST buf, NULL);
         xmlNewProp(node2, BAD_CAST "source", BAD_CAST mod_src_name(modsrc));
-        snprintf(buf, BUFSIZE, "%f", modamt);
+        snprintf(buf, CHARBUFSIZE, "%f", modamt);
         xmlNewProp(node2, BAD_CAST "amount", BAD_CAST buf);
     }
 
     if (param == PATCH_PARAM_AMPLITUDE)
     {
-        patch_get_mod_src(patch_id, PATCH_PARAM_AMPLITUDE,
-                                    EG_MOD_SLOT, &modsrc);
+        modsrc = patch_param_get_mod_src(patch_id, PATCH_PARAM_AMPLITUDE,
+                                                   EG_MOD_SLOT);
         node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Env", NULL);
         xmlNewProp(node2, BAD_CAST "source", BAD_CAST mod_src_name(modsrc));
     }
@@ -189,15 +186,100 @@ dish_file_write_param(xmlNodePtr nodeparent, int patch_id,
 
 
 static int
+dish_file_write_bool(xmlNodePtr nodeparent, int patch_id,
+                                                PatchBoolType bool_type)
+{
+    xmlNodePtr  node1;
+    xmlNodePtr  node2;
+    char buf[CHARBUFSIZE];
+    const char* nodestr;
+
+    bool    set;
+    float   thresh;
+    int     modsrc;
+
+    switch(bool_type)
+    {
+    case PATCH_BOOL_PORTAMENTO:
+        nodestr = "Portamento";
+        break;
+    case PATCH_BOOL_MONO:
+        nodestr = "Mono";
+        break;
+    case PATCH_BOOL_LEGATO:
+        nodestr = "Legato";
+        break;
+    default:
+        return -1;
+    }
+
+    patch_bool_get_all(patch_id, bool_type, &set, &thresh, &modsrc);
+
+    node1 = xmlNewTextChild(nodeparent, NULL, BAD_CAST nodestr, NULL);
+
+    xmlNewProp(node1, BAD_CAST "active",
+                        BAD_CAST ((set) ? "true" : "false"));
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Mod", NULL);
+
+    xmlNewProp(node2, BAD_CAST "source", BAD_CAST mod_src_name(modsrc));
+
+    snprintf(buf, CHARBUFSIZE, "%f", thresh);
+    xmlNewProp(node2, BAD_CAST "threshold", BAD_CAST buf);
+
+    return 0;
+}
+
+
+static int
+dish_file_write_float(xmlNodePtr nodeparent, int patch_id,
+                                                PatchFloatType float_type)
+{
+    xmlNodePtr  node1;
+    xmlNodePtr  node2;
+    char buf[CHARBUFSIZE];
+    const char* nodestr;
+
+    float   val;
+    float   modamt;
+    int     modsrc;
+
+    switch(float_type)
+    {
+    case PATCH_FLOAT_PORTAMENTO_TIME:
+        nodestr = "Portamento_time";
+        break;
+    default:
+        return -1;
+    }
+
+    patch_float_get_all(patch_id, float_type, &val, &modamt, &modsrc);
+
+    node1 = xmlNewTextChild(nodeparent, NULL, BAD_CAST nodestr, NULL);
+
+    snprintf(buf, CHARBUFSIZE, "%f", val);
+    xmlNewProp(node1,   BAD_CAST "value",   BAD_CAST buf);
+
+    node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Mod", NULL);
+
+    xmlNewProp(node2, BAD_CAST "source", BAD_CAST mod_src_name(modsrc));
+
+    snprintf(buf, CHARBUFSIZE, "%f", modamt);
+    xmlNewProp(node2, BAD_CAST "amount", BAD_CAST buf);
+
+    return 0;
+}
+
+
+static int
 dish_file_write_eg(xmlNodePtr nodeparent, int patch_id, int eg_id)
 {
     xmlNodePtr  node1;
-    char buf[BUFSIZE];
+    char buf[CHARBUFSIZE];
     bool active;
     float val;
 
-    if (patch_get_env_on(patch_id, eg_id, &active) == -1)
-        return -1;
+    active = patch_get_env_active(patch_id, eg_id);
 
     node1 = xmlNewTextChild(nodeparent, NULL,
                             BAD_CAST mod_src_name(eg_id), NULL);
@@ -205,32 +287,32 @@ dish_file_write_eg(xmlNodePtr nodeparent, int patch_id, int eg_id)
     xmlNewProp(node1,   BAD_CAST "active",
                         BAD_CAST (active ? "true" : "false"));
 
-    patch_get_env_delay(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_delay(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "delay",   BAD_CAST buf);
 
-    patch_get_env_attack(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_attack(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "attack",   BAD_CAST buf);
 
-    patch_get_env_hold(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_hold(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "hold",   BAD_CAST buf);
 
-    patch_get_env_decay(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_decay(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "decay",   BAD_CAST buf);
 
-    patch_get_env_sustain(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_sustain(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "sustain",   BAD_CAST buf);
 
-    patch_get_env_release(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_release(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "release",   BAD_CAST buf);
 
-    patch_get_env_key_amt(patch_id, eg_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_env_key_amt(patch_id, eg_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node1,   BAD_CAST "key_tracking",   BAD_CAST buf);
 
     return 0;
@@ -243,7 +325,7 @@ dish_file_write_lfo(xmlNodePtr nodeparent, int patch_id, int lfo_id)
     xmlNodePtr  node1;
     xmlNodePtr  node2;
     xmlNodePtr  node3;
-    char buf[BUFSIZE];
+    char buf[CHARBUFSIZE];
     bool state;
     float val;
     const char** shapes = names_lfo_shapes_get();
@@ -253,8 +335,7 @@ dish_file_write_lfo(xmlNodePtr nodeparent, int patch_id, int lfo_id)
     float   mod2amt;
     LFOShape shape;
 
-    if (patch_get_lfo_on(patch_id, lfo_id, &state) == -1)
-        return -1;
+    state = patch_get_lfo_active(patch_id, lfo_id);
 
     node1 = xmlNewTextChild(nodeparent, NULL,
                             BAD_CAST mod_src_name(lfo_id), NULL);
@@ -263,68 +344,68 @@ dish_file_write_lfo(xmlNodePtr nodeparent, int patch_id, int lfo_id)
                         BAD_CAST (state ? "true" : "false"));
 
     node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Frequency", NULL);
-    patch_get_lfo_freq(patch_id, lfo_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_lfo_freq(patch_id, lfo_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node2,   BAD_CAST "hrtz", BAD_CAST buf);
 
-    patch_get_lfo_beats(patch_id, lfo_id, &val);
-    snprintf(buf, BUFSIZE, "%f", val);
+    val = patch_get_lfo_sync_beats(patch_id, lfo_id);
+    snprintf(buf, CHARBUFSIZE, "%f", val);
     xmlNewProp(node2,   BAD_CAST "beats", BAD_CAST buf);
 
-    patch_get_lfo_sync(patch_id, lfo_id, &state);
+    val = patch_get_lfo_sync(patch_id, lfo_id);
     xmlNewProp(node2,   BAD_CAST "sync",
                         BAD_CAST (state ? "true" : "false"));
 
-    patch_get_lfo_fm1_src(patch_id, lfo_id, &mod1src);
-    patch_get_lfo_fm1_amt(patch_id, lfo_id, &mod1amt);
-    patch_get_lfo_fm2_src(patch_id, lfo_id, &mod2src);
-    patch_get_lfo_fm2_amt(patch_id, lfo_id, &mod2amt);
+    mod1src = patch_get_lfo_fm1_src(patch_id, lfo_id);
+    mod1amt = patch_get_lfo_fm1_amt(patch_id, lfo_id);
+    mod2src = patch_get_lfo_fm2_src(patch_id, lfo_id);
+    mod2amt = patch_get_lfo_fm2_amt(patch_id, lfo_id);
 
     node3 = xmlNewTextChild(node2, NULL, BAD_CAST "Mod1", NULL);
     xmlNewProp(node3, BAD_CAST "source", BAD_CAST mod_src_name(mod1src));
-    snprintf(buf, BUFSIZE, "%f", mod1amt);
+    snprintf(buf, CHARBUFSIZE, "%f", mod1amt);
     xmlNewProp(node3, BAD_CAST "amount", BAD_CAST buf);
 
     node3 = xmlNewTextChild(node2, NULL, BAD_CAST "Mod2", NULL);
     xmlNewProp(node3, BAD_CAST "source", BAD_CAST mod_src_name(mod2src));
-    snprintf(buf, BUFSIZE, "%f", mod2amt);
+    snprintf(buf, CHARBUFSIZE, "%f", mod2amt);
     xmlNewProp(node3, BAD_CAST "amount", BAD_CAST buf);
 
 
     node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Amplitude", NULL);
 
-    patch_get_lfo_shape(patch_id, lfo_id, &shape);
+    shape = patch_get_lfo_shape(patch_id, lfo_id);
     xmlNewProp(node2,   BAD_CAST "shape", BAD_CAST shapes[shape]);
 
-    patch_get_lfo_positive(patch_id, lfo_id, &state);
+    state = patch_get_lfo_positive(patch_id, lfo_id);
     xmlNewProp(node2,   BAD_CAST "positive",
                         BAD_CAST (state ? "true" : "false"));
 
     /* assured by caller that lfo_id IS an lfo_id */
     if (!mod_src_is_global(lfo_id))
     {
-        patch_get_lfo_delay(patch_id, lfo_id, &val);
-        snprintf(buf, BUFSIZE, "%f", val);
+        val = patch_get_lfo_delay(patch_id, lfo_id);
+        snprintf(buf, CHARBUFSIZE, "%f", val);
         xmlNewProp(node2,   BAD_CAST "delay",   BAD_CAST buf);
 
-        patch_get_lfo_attack(patch_id, lfo_id, &val);
-        snprintf(buf, BUFSIZE, "%f", val);
+        val = patch_get_lfo_attack(patch_id, lfo_id);
+        snprintf(buf, CHARBUFSIZE, "%f", val);
         xmlNewProp(node2,   BAD_CAST "attack",  BAD_CAST buf);
     }
 
-    patch_get_lfo_am1_src(patch_id, lfo_id, &mod1src);
-    patch_get_lfo_am1_amt(patch_id, lfo_id, &mod1amt);
-    patch_get_lfo_am2_src(patch_id, lfo_id, &mod2src);
-    patch_get_lfo_am2_amt(patch_id, lfo_id, &mod2amt);
+    mod1src = patch_get_lfo_am1_src(patch_id, lfo_id);
+    mod1amt = patch_get_lfo_am1_amt(patch_id, lfo_id);
+    mod2src = patch_get_lfo_am2_src(patch_id, lfo_id);
+    mod2amt = patch_get_lfo_am2_amt(patch_id, lfo_id);
 
     node3 = xmlNewTextChild(node2, NULL, BAD_CAST "Mod1", NULL);
     xmlNewProp(node3, BAD_CAST "source", BAD_CAST mod_src_name(mod1src));
-    snprintf(buf, BUFSIZE, "%f", mod1amt);
+    snprintf(buf, CHARBUFSIZE, "%f", mod1amt);
     xmlNewProp(node3, BAD_CAST "amount", BAD_CAST buf);
 
     node3 = xmlNewTextChild(node2, NULL, BAD_CAST "Mod2", NULL);
     xmlNewProp(node3, BAD_CAST "source", BAD_CAST mod_src_name(mod2src));
-    snprintf(buf, BUFSIZE, "%f", mod2amt);
+    snprintf(buf, CHARBUFSIZE, "%f", mod2amt);
     xmlNewProp(node3, BAD_CAST "amount", BAD_CAST buf);
 
     return 0;
@@ -341,7 +422,7 @@ int dish_file_write(const char *name)
     xmlNodePtr  node1;
     xmlNodePtr  node2;
 
-    char    buf[BUFSIZE];
+    char    buf[CHARBUFSIZE];
     int     i, j;
     int*    patch_id;
     int     patch_count;
@@ -361,9 +442,13 @@ int dish_file_write(const char *name)
      */
     node1 = xmlNewTextChild(noderoot, NULL, BAD_CAST "Master", NULL);
 
-    snprintf(buf, BUFSIZE, "%f", mixer_get_amplitude());
+    snprintf(buf, CHARBUFSIZE, "%f", mixer_get_amplitude());
     xmlNewProp(node1, BAD_CAST "level", BAD_CAST buf);
 
+    snprintf(buf, CHARBUFSIZE, "%d", patch_get_samplerate());
+    xmlNewProp(node1, BAD_CAST "samplerate", BAD_CAST buf);
+
+
     /*  ------------------------
         patches
      */
@@ -379,7 +464,7 @@ int dish_file_write(const char *name)
         xmlNewProp(nodepatch,   BAD_CAST "name",
                                 BAD_CAST patch_get_name(patch_id[i]));
 
-        snprintf(buf, BUFSIZE, "%d", patch_get_channel(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_channel(patch_id[i]));
         xmlNewProp(nodepatch,   BAD_CAST "channel", BAD_CAST buf);
 
         /*  ------------------------
@@ -398,42 +483,51 @@ int dish_file_write(const char *name)
         /* sample play */
         node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Play", NULL);
 
-        snprintf(buf, BUFSIZE, "%d",
+        snprintf(buf, CHARBUFSIZE, "%d",
                     patch_get_mark_frame(patch_id[i], WF_MARK_PLAY_START));
         xmlNewProp(node2,   BAD_CAST "start", BAD_CAST buf);
 
-        snprintf(buf, BUFSIZE, "%d",
+        snprintf(buf, CHARBUFSIZE, "%d",
                     patch_get_mark_frame(patch_id[i], WF_MARK_PLAY_STOP));
         xmlNewProp(node2,   BAD_CAST "stop", BAD_CAST buf);
 
-        snprintf(buf, BUFSIZE, "%d", patch_get_fade_samples(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d",
+                                patch_get_fade_samples(patch_id[i]));
         xmlNewProp(node2,   BAD_CAST "fade_samples", BAD_CAST buf);
 
         /* sample loop */
         node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Loop", NULL);
 
-        snprintf(buf, BUFSIZE, "%d",
+        snprintf(buf, CHARBUFSIZE, "%d",
                     patch_get_mark_frame(patch_id[i], WF_MARK_LOOP_START));
         xmlNewProp(node2,   BAD_CAST "start", BAD_CAST buf);
 
-        snprintf(buf, BUFSIZE, "%d",
+        snprintf(buf, CHARBUFSIZE, "%d",
                     patch_get_mark_frame(patch_id[i], WF_MARK_LOOP_STOP));
         xmlNewProp(node2,   BAD_CAST "stop", BAD_CAST buf);
 
-        snprintf(buf, BUFSIZE, "%d", patch_get_xfade_samples(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d",
+                    patch_get_xfade_samples(patch_id[i]));
         xmlNewProp(node2,   BAD_CAST "xfade_samples", BAD_CAST buf);
 
         /* sample note */
         node2 = xmlNewTextChild(node1, NULL, BAD_CAST "Note", NULL);
 
-        snprintf(buf, BUFSIZE, "%d", patch_get_note(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_root_note(patch_id[i]));
         xmlNewProp(node2,   BAD_CAST "root", BAD_CAST buf);
 
-        snprintf(buf, BUFSIZE, "%d", patch_get_lower_note(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_lower_note(patch_id[i]));
         xmlNewProp(node2,   BAD_CAST "lower", BAD_CAST buf);
 
-        snprintf(buf, BUFSIZE, "%d", patch_get_upper_note(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_upper_note(patch_id[i]));
         xmlNewProp(node2,   BAD_CAST "upper", BAD_CAST buf);
+        
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_lower_vel(patch_id[i]));
+        xmlNewProp(node2,   BAD_CAST "velocity_lower", BAD_CAST buf);
+
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_upper_vel(patch_id[i]));
+        xmlNewProp(node2,   BAD_CAST "velocity_upper", BAD_CAST buf);
+
 
         /*  ------------------------
             amplitude
@@ -463,23 +557,19 @@ int dish_file_write(const char *name)
         node1 = xmlNewTextChild(nodepatch, NULL, BAD_CAST "Voice", NULL);
 
         /* voice cut */
-        snprintf(buf, BUFSIZE, "%d", patch_get_cut(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_cut(patch_id[i]));
         xmlNewProp(node1,   BAD_CAST "cut", BAD_CAST buf);
 
         /* voice cut by */
-        snprintf(buf, BUFSIZE, "%d", patch_get_cut_by(patch_id[i]));
+        snprintf(buf, CHARBUFSIZE, "%d", patch_get_cut_by(patch_id[i]));
         xmlNewProp(node1,   BAD_CAST "cut_by", BAD_CAST buf);
 
         /* voice portamento */
-        xmlNewProp(node1,   BAD_CAST "portamento",
-                            BAD_CAST (patch_get_portamento(patch_id[i])
-                                        ? "true"
-                                        : "false"));
+        dish_file_write_bool(node1, patch_id[i], PATCH_BOOL_PORTAMENTO);
 
         /* voice portamento_time */
-        snprintf(buf, BUFSIZE, "%f",
-                            patch_get_portamento_time(patch_id[i]));
-        xmlNewProp(node1,   BAD_CAST "portamento_time", BAD_CAST buf);
+        dish_file_write_float(node1, patch_id[i], 
+                                            PATCH_FLOAT_PORTAMENTO_TIME);
 
         /* voice monophonic */
         xmlNewProp(node1,   BAD_CAST "monophonic",
@@ -488,10 +578,7 @@ int dish_file_write(const char *name)
                                         : "false"));
 
         /* voice legato */
-        xmlNewProp(node1,   BAD_CAST "legato",
-                            BAD_CAST (patch_get_legato(patch_id[i])
-                                        ? "true"
-                                        : "false"));
+        dish_file_write_bool(node1, patch_id[i], PATCH_BOOL_LEGATO);
 
         /*  ------------------------
             envelopes
@@ -540,6 +627,14 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
     char* filename = 0;
     bool sample_loaded = false;
 
+    double sr_ratio = 1.0;
+
+    if (dish_file_samplerate
+     && dish_file_samplerate != patch_get_samplerate())
+        sr_ratio = patch_get_samplerate() / (double)dish_file_samplerate;
+
+debug("sr_ratio:%f\n",sr_ratio);
+
     int mode = PATCH_PLAY_SINGLESHOT;
 
     if ((prop = xmlGetProp(node, BAD_CAST "file")))
@@ -557,15 +652,13 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
             mode = PATCH_PLAY_LOOP | PATCH_PLAY_PINGPONG;
         else
         {
-            errmsg("invalid play mode:%s\n", prop);
+            msg_log(MSG_ERROR, "Invalid sample play mode:%s\n", prop);
         }
     }
 
     if ((prop = xmlGetProp(node, BAD_CAST "reverse"))
                                                 && xmlstr_to_bool(prop))
         mode |= PATCH_PLAY_REVERSE;
-    else
-        mode |= PATCH_PLAY_FORWARD;
 
     if ((prop = xmlGetProp(node, BAD_CAST "to_end"))
                                                 && xmlstr_to_bool(prop))
@@ -586,7 +679,6 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
             int raw_samplerate = 0;
             int raw_channels = 0;
             int sndfile_format = 0;
-            int err;
 
             if (xmlStrcmp(node1->name, BAD_CAST "Raw") == 0)
             {
@@ -603,14 +695,13 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
                         sndfile_format = n;
             }
 
-            err = patch_sample_load(patch_id, filename,
+            if (patch_sample_load(patch_id, filename,
                                               raw_samplerate,
                                               raw_channels,
-                                              sndfile_format);
-            if (err)
+                                              sndfile_format) < 0)
             {
-                errmsg("failed to load sample:%s\n", (const char*)filename);
-                return 0;
+                msg_log(MSG_ERROR, "failed to load sample: %s error (%s)\n",
+                    filename, pf_error_str(pf_error_get()));
             }
 
             free(filename);
@@ -625,17 +716,17 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
                 if (sscanf((const char*)prop, "%d", &s) == 1)
                     patch_set_mark_frame_expand(patch_id,
                                                 WF_MARK_PLAY_START,
-                                                s, NULL);
+                                                s * sr_ratio, NULL);
             }
             if ((prop = xmlGetProp(node1, BAD_CAST "stop")))
                 if (sscanf((const char*)prop, "%d", &s) == 1)
                     patch_set_mark_frame_expand(patch_id,
                                                 WF_MARK_PLAY_STOP,
-                                                s, NULL);
+                                                s * sr_ratio, NULL);
 
             if ((prop = xmlGetProp(node1, BAD_CAST "fade_samples")))
                 if (sscanf((const char*)prop, "%d", &s) == 1)
-                    patch_set_fade_samples(patch_id, s);
+                    patch_set_fade_samples(patch_id, s * sr_ratio);
         }
         else if (xmlStrcmp(node1->name, BAD_CAST "Loop") == 0)
         {
@@ -643,25 +734,25 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
                 if (sscanf((const char*)prop, "%d", &s) == 1)
                     patch_set_mark_frame_expand(patch_id,
                                                 WF_MARK_LOOP_START,
-                                                s, NULL);
+                                                s * sr_ratio, NULL);
 
             if ((prop = xmlGetProp(node1, BAD_CAST "stop")))
                 if (sscanf((const char*)prop, "%d", &s) == 1)
                     patch_set_mark_frame_expand(patch_id,
                                                 WF_MARK_LOOP_STOP,
-                                                s, NULL);
+                                                s * sr_ratio, NULL);
 
             if ((prop = xmlGetProp(node1, BAD_CAST "xfade_samples")))
                 if (sscanf((const char*)prop, "%d", &s) == 1)
-                    patch_set_xfade_samples(patch_id, s);
+                    patch_set_xfade_samples(patch_id, s * sr_ratio);
         }
         else if (xmlStrcmp(node1->name, BAD_CAST "Note") == 0)
         {
-            int lower, root, upper;
+            int lower, root, upper, lower_vel, upper_vel;
 
             if ((prop = xmlGetProp(node1, BAD_CAST "root")))
                 if (sscanf((const char*)prop, "%d", &root) == 1)
-                    patch_set_note(patch_id,  root);
+                    patch_set_root_note(patch_id,  root);
 
             if ((prop = xmlGetProp(node1, BAD_CAST "lower")))
                 if (sscanf((const char*)prop, "%d", &lower) == 1)
@@ -670,10 +761,19 @@ int dish_file_read_sample(xmlNodePtr node, int patch_id)
             if ((prop = xmlGetProp(node1, BAD_CAST "upper")))
                 if (sscanf((const char*)prop, "%d", &upper) == 1)
                     patch_set_upper_note(patch_id,  upper);
+            
+            if ((prop = xmlGetProp(node1, BAD_CAST "velocity_lower")))
+                if (sscanf((const char*)prop, "%d", &lower_vel) == 1)
+                    patch_set_lower_vel(patch_id,  lower_vel);
+
+            if ((prop = xmlGetProp(node1, BAD_CAST "velocity_upper")))
+                if (sscanf((const char*)prop, "%d", &upper_vel) == 1)
+                    patch_set_upper_vel(patch_id,  upper_vel);
         }
         else
         {
-            errmsg("ignoring:%s\n", node1->name);
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
         }
     }
 
@@ -689,10 +789,8 @@ int dish_file_read_eg(xmlNodePtr node, int patch_id)
     if ((eg_id = mod_src_id((const char*)node->name, MOD_SRC_EG)) < 0)
         return -1;
 
-debug("loading eg with id:%d\n", eg_id);
-
     if ((prop = xmlGetProp(node, BAD_CAST "active")))
-        patch_set_env_on(patch_id, eg_id, xmlstr_to_bool(prop));
+        patch_set_env_active(patch_id, eg_id, xmlstr_to_bool(prop));
 
     if ((prop = xmlGetProp(node, BAD_CAST "delay")))
         if (sscanf((const char*)prop, "%f", &n) == 1)
@@ -737,7 +835,7 @@ int dish_file_read_lfo_freq_data(xmlNodePtr node, int patch_id, int lfo_id)
 
     if ((prop = xmlGetProp(node, BAD_CAST "beats")))
         if (sscanf((const char*)prop, "%f", &n) == 1)
-            patch_set_lfo_beats(patch_id, lfo_id, n);
+            patch_set_lfo_sync_beats(patch_id, lfo_id, n);
 
     if ((prop = xmlGetProp(node, BAD_CAST "sync")))
         patch_set_lfo_sync(patch_id, lfo_id, xmlstr_to_bool(prop));
@@ -771,7 +869,8 @@ int dish_file_read_lfo_freq_data(xmlNodePtr node, int patch_id, int lfo_id)
         }
         else
         {
-            errmsg("ignoring:%s\n", (const char*)node1->name);
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
         }
     }
 
@@ -832,7 +931,8 @@ int dish_file_read_lfo_amp_data(xmlNodePtr node, int patch_id, int lfo_id)
         }
         else
         {
-            errmsg("ignoring:%s\n", (const char*)node1->name);
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
         }
     }
 
@@ -857,7 +957,7 @@ debug("lfo id:%d\n", lfo_id);
     }
 
     if ((prop = xmlGetProp(node, BAD_CAST "active")))
-        patch_set_lfo_on(patch_id, lfo_id, xmlstr_to_bool(prop));
+        patch_set_lfo_active(patch_id, lfo_id, xmlstr_to_bool(prop));
 
     for (   node1 = node->children;
             node1 != NULL;
@@ -876,7 +976,8 @@ debug("lfo id:%d\n", lfo_id);
         }
         else
         {
-            errmsg("ignoring:%s\n", (const char*)node1->name);
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
         }
     }
 
@@ -918,11 +1019,11 @@ int dish_file_read_param(xmlNodePtr node,   int patch_id,
 
     if ((prop = xmlGetProp(node, BAD_CAST "velocity_sensing")))
         if (sscanf((const char*)prop, "%f", &n) == 1)
-            patch_set_vel_amount(patch_id, param, n);
+            patch_param_set_vel_amount(patch_id, param, n);
 
     if ((prop = xmlGetProp(node, BAD_CAST "key_tracking")))
         if (sscanf((const char*)prop, "%f", &n) == 1)
-            patch_set_key_amount(patch_id, param, n);
+            patch_param_set_key_amount(patch_id, param, n);
 
     for (   node1 = node->children;
             node1 != NULL;
@@ -939,23 +1040,102 @@ int dish_file_read_param(xmlNodePtr node,   int patch_id,
             --slot; /* slot 0 is named as MOD1 */
 
             if ((prop = xmlGetProp(node1, BAD_CAST "source")))
-                patch_set_mod_src(patch_id, param, slot,
-                    mod_src_id((const char*)prop, MOD_SRC_ALL));
+                patch_param_set_mod_src(patch_id, param, slot,
+                            mod_src_id((const char*)prop, MOD_SRC_ALL));
 
             if ((prop = xmlGetProp(node1, BAD_CAST "amount")))
                 if (sscanf((const char*)prop, "%f", &n) == 1)
-                    patch_set_mod_amt(patch_id, param, slot, n);
+                    patch_param_set_mod_amt(patch_id, param, slot, n);
         }
         else if ((param == PATCH_PARAM_AMPLITUDE
                 && xmlStrcmp(node1->name, BAD_CAST "Env") == 0))
         {
             if ((prop = xmlGetProp(node1, BAD_CAST "source")))
-                patch_set_mod_src(patch_id, param, EG_MOD_SLOT,
-                    mod_src_id((const char*)prop, MOD_SRC_ALL));
+                patch_param_set_mod_src(patch_id, param, EG_MOD_SLOT,
+                            mod_src_id((const char*)prop, MOD_SRC_ALL));
         }
         else
         {
-            errmsg("ignoring:%s\n", (const char*)node1->name);
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
+        }
+    }
+
+    return 0;
+}
+
+
+int dish_file_read_bool(xmlNodePtr node, int patch_id,
+                                            PatchBoolType bool_type)
+{
+    float       n;
+    xmlChar*    prop;
+    xmlNodePtr  node1;
+
+    if ((prop = xmlGetProp(node, BAD_CAST "active")))
+        patch_bool_set_active(patch_id, bool_type, xmlstr_to_bool(prop));
+
+    for (   node1 = node->children;
+            node1 != NULL;
+            node1 = node1->next)
+    {
+        if (node1->type != XML_ELEMENT_NODE)
+            continue;
+
+        if (xmlStrcmp(node1->name, BAD_CAST "Mod") == 0)
+        {
+            if ((prop = xmlGetProp(node1, BAD_CAST "source")))
+                patch_bool_set_mod_src(patch_id, bool_type,
+                            mod_src_id((const char*)prop, MOD_SRC_GLOBALS));
+
+            if ((prop = xmlGetProp(node1, BAD_CAST "threshold")))
+                if (sscanf((const char*)prop, "%f", &n) == 1)
+                    patch_bool_set_thresh(patch_id, bool_type, n);
+        }
+        else
+        {
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
+        }
+    }
+
+    return 0;
+}
+
+
+
+int dish_file_read_float(xmlNodePtr node, int patch_id,
+                                            PatchFloatType float_type)
+{
+    float       n;
+    xmlChar*    prop;
+    xmlNodePtr  node1;
+
+    if ((prop = xmlGetProp(node, BAD_CAST "active")))
+    /* FIXME: needs value bounds testing */
+        patch_float_set_value(patch_id, float_type, xmlstr_to_bool(prop));
+
+    for (   node1 = node->children;
+            node1 != NULL;
+            node1 = node1->next)
+    {
+        if (node1->type != XML_ELEMENT_NODE)
+            continue;
+
+        if (xmlStrcmp(node1->name, BAD_CAST "Mod") == 0)
+        {
+            if ((prop = xmlGetProp(node1, BAD_CAST "source")))
+                patch_float_set_mod_src(patch_id, float_type,
+                            mod_src_id((const char*)prop, MOD_SRC_GLOBALS));
+
+            if ((prop = xmlGetProp(node1, BAD_CAST "amount")))
+                if (sscanf((const char*)prop, "%f", &n) == 1)
+                    patch_float_set_mod_amt(patch_id, float_type, n);
+        }
+        else
+        {
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
         }
     }
 
@@ -966,7 +1146,8 @@ int dish_file_read_param(xmlNodePtr node,   int patch_id,
 int dish_file_read_voice(xmlNodePtr node, int patch_id)
 {
     xmlChar*    prop;
-    float n;
+    xmlNodePtr  node1;
+
     int i;
 
     if ((prop = xmlGetProp(node, BAD_CAST "cut")))
@@ -977,18 +1158,35 @@ int dish_file_read_voice(xmlNodePtr node, int patch_id)
         if (sscanf((const char*)prop, "%d", &i))
             patch_set_cut_by(patch_id, i);
 
-    if ((prop = xmlGetProp(node, BAD_CAST "portamento")))
-        patch_set_portamento(patch_id, xmlstr_to_bool(prop));
-
-    if ((prop = xmlGetProp(node, BAD_CAST "portamento_time")))
-        if (sscanf((const char*)prop, "%f", &n))
-            patch_set_portamento_time(patch_id, n);
-
     if ((prop = xmlGetProp(node, BAD_CAST "monophonic")))
         patch_set_monophonic(patch_id, xmlstr_to_bool(prop));
 
-    if ((prop = xmlGetProp(node, BAD_CAST "legato")))
-        patch_set_legato(patch_id, xmlstr_to_bool(prop));
+    for (   node1 = node->children;
+            node1 != NULL;
+            node1 = node1->next)
+    {
+        if (node1->type != XML_ELEMENT_NODE)
+            continue;
+
+        if (xmlStrcmp(node1->name, BAD_CAST "Portamento") == 0)
+        {
+            dish_file_read_bool(node1, patch_id, PATCH_BOOL_PORTAMENTO);
+        }
+        else if (xmlStrcmp(node1->name, BAD_CAST "Portamento_time") == 0)
+        {
+            dish_file_read_float(node1, patch_id,
+                                            PATCH_FLOAT_PORTAMENTO_TIME);
+        }
+        else if (xmlStrcmp(node1->name, BAD_CAST "Legato") == 0)
+        {
+            dish_file_read_bool(node1, patch_id, PATCH_BOOL_LEGATO);
+        }
+        else
+        {
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
+        }
+    }
 
     return 0;
 }
@@ -1041,9 +1239,19 @@ int dish_file_read(const char *path)
 
         if (xmlStrcmp(node1->name, BAD_CAST "Master") == 0)
         {
+            int sr;
+
             if ((prop = xmlGetProp(node1, BAD_CAST "level")))
                 if (sscanf((const char*)prop, "%f", &n) == 1)
                     mixer_set_amplitude(n);
+
+            if ((prop = xmlGetProp(node1, BAD_CAST "samplerate")))
+            {
+                if (sscanf((const char*)prop, "%d", &sr) == 1)
+                    dish_file_samplerate = sr;
+                else
+                    dish_file_samplerate = 0;
+            }
         }
         else if (xmlStrcmp(node1->name, BAD_CAST "Patch") == 0)
         {
@@ -1125,14 +1333,16 @@ int dish_file_read(const char *path)
                             dish_file_read_lfo(node2, patch_id);
                     else
                     {
-                        errmsg("ignoring:%s\n", (const char*)node2->name);
+                        msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                            (const char*)node2->name);
                     }
                 }
             }
         }
         else
         {
-            errmsg("ignoring:%s\n", (const char*)node1->name);
+            msg_log(MSG_WARNING, "ignoring XML NODE: %s\n",
+                                    (const char*)node1->name);
         }
     }
 
diff --git a/src/dish_file.h b/libpetrifui/dish_file.h
similarity index 100%
rename from src/dish_file.h
rename to libpetrifui/dish_file.h
diff --git a/libpetrifui/msg_log.c b/libpetrifui/msg_log.c
new file mode 100644
index 0000000..56d7885
--- /dev/null
+++ b/libpetrifui/msg_log.c
@@ -0,0 +1,170 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+
+#include "petri-foo.h"
+#include "msg_log.h"
+
+
+static bool         msg_log_notification_state = false;
+static msg_log_cb   msg_log_callback = 0;
+
+
+void timestamp(char* buf, int buflen)
+{
+    time_t ltime;
+    struct tm *Tm;
+    struct timeval detail_time;
+    int n;
+
+    if (!buf)
+        return;
+
+    if (buflen < 13)
+    {
+        *buf = '\0';
+        return;
+    }
+
+    gettimeofday(&detail_time,NULL);
+    ltime = time(NULL);
+    Tm=localtime(&ltime);
+
+    n = snprintf(buf, buflen, "%02d:%02d:%02d.%03d",
+                    Tm->tm_hour, Tm->tm_min, Tm->tm_sec,
+                    (int)(detail_time.tv_usec / 1000.0f));
+
+    if (n >= buflen)
+        snprintf(buf, buflen, "--:--:--.---");
+}
+
+
+char* strconcat(const char* str1, const char* str2)
+{
+    char* str = malloc(strlen(str1) + strlen(str2) + 1);
+
+    if (!str)
+        return 0;
+
+    strcpy(str, str1);
+    strcat(str, str2);
+
+    return str;
+}
+
+
+int msg_log(int type, const char* fmt, ...)
+{
+    const char* types[] = {
+        "Debug",
+        "Message",
+        "Warning",
+        "ERROR",
+        "CRITICAL!"
+    };
+
+    char    msg[1024];
+    char    tm[20];
+    char    tmp[1024];
+
+    va_list ap;
+
+    int rc = 0;
+    int base_type = type & MSG_TYPE_MASK;
+    int out_type = type & MSG_FLAG_OUTPUT_MASK;
+
+    if (!out_type)
+        goto skip;
+
+    if (base_type <= MSG_INVALID || base_type >= MSG_TYPE_XXX)
+    {
+        char* newfmt;
+        newfmt = strconcat("Invalid log message: ", fmt);
+
+        va_start(ap, fmt);
+        msg_log(MSG_CRITICAL, newfmt, ap);
+        va_end(ap);
+
+        free(newfmt);
+
+        return -1;
+    }
+
+    timestamp(tm, 20);
+
+    va_start(ap, fmt);
+
+    if (vsnprintf(tmp, 1023, fmt, ap) >= 1023)
+        tmp[1023] = '\0';
+
+    va_end(ap);
+
+    rc = snprintf(msg, 1023, "%s %s: %s ", tm, types[base_type], tmp);
+
+    if (rc >= 1023)
+    {
+        rc = 1023;
+        msg[rc] = '\0';
+    }
+
+    if (out_type & MSG_FLAG_STDOUT)
+        fprintf(stdout, "%s", msg);
+
+    if (out_type & MSG_FLAG_STDERR)
+        fprintf(stderr, "%s", msg);
+
+    if ((out_type & MSG_FLAG_STDUI) && msg_log_callback)
+        msg_log_callback(msg, base_type);
+
+skip:
+
+    if (type & MSG_FLAG_NOTIFY)
+        msg_log_notification_state = true;
+
+    return rc;
+}
+
+
+/*  were there any errors? */
+bool msg_log_get_notification_state(void)
+{
+    return msg_log_notification_state;
+}
+
+
+/*  reset error status */
+void msg_log_reset_notification_state(void)
+{
+    msg_log_notification_state = false;
+}
+
+
+void msg_log_set_message_cb(msg_log_cb msgcb)
+{
+    msg_log_callback = msgcb;
+}
+
diff --git a/libpetrifui/msg_log.h b/libpetrifui/msg_log.h
new file mode 100644
index 0000000..83f0612
--- /dev/null
+++ b/libpetrifui/msg_log.h
@@ -0,0 +1,94 @@
+/*  Petri-Foo is a fork of the Specimen audio sampler.
+
+    Copyright 2011 James W. Morris
+
+    This file is part of Petri-Foo.
+
+    Petri-Foo is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef MSG_LOG_H
+#define MSG_LOG_H
+
+/*  MSG_LOG
+
+    Logging of messages useful to Petri-Foo user. Not to be confused with
+    messages useful to developers. Messages such as errors when user asks
+    Petri-Foo to load a bank or sample, etc.
+
+    *** NOT for usage by RT thread ***
+ */
+
+
+#include <stdbool.h>
+
+
+
+/*  type of the callback that will handle the string formed by
+    logging a message:
+ */
+typedef void (*msg_log_cb)(const char* msg, int msg_base_type);
+
+
+enum
+{
+/* invalid message type */
+    MSG_INVALID =       -1,
+
+/* base message types: */
+    MSG_TYPE_DEBUG =    0,
+    MSG_TYPE_MESSAGE,
+    MSG_TYPE_WARNING,
+    MSG_TYPE_ERROR,
+    MSG_TYPE_CRITICAL,
+
+/* used for checking valid base message types */
+    MSG_TYPE_XXX,
+
+/* message type mask */
+    MSG_TYPE_MASK =         0x000f,
+
+/* message flags: */
+    MSG_FLAG_NOTIFY =       0x0010, /* sets notification state */
+
+    MSG_FLAG_STDOUT =       0x0100, /* output to stdout */
+    MSG_FLAG_STDERR =       0x0200, /* output to stderr */
+    MSG_FLAG_STDUI =        0x0400, /* output to ie gui */
+
+    MSG_FLAG_OUTPUT_MASK =  0x0f00,
+
+/* the actual message types generally used */
+    MSG_DEBUG =     MSG_TYPE_DEBUG      | MSG_FLAG_STDOUT,
+    MSG_MESSAGE =   MSG_TYPE_MESSAGE    | MSG_FLAG_STDOUT | MSG_FLAG_STDUI,
+    MSG_WARNING =   MSG_TYPE_WARNING    | MSG_FLAG_STDOUT | MSG_FLAG_STDUI,
+
+    MSG_ERROR =     MSG_TYPE_ERROR      | MSG_FLAG_NOTIFY 
+                  | MSG_FLAG_STDERR     | MSG_FLAG_STDUI,
+
+    MSG_CRITICAL =  MSG_TYPE_CRITICAL   | MSG_FLAG_NOTIFY
+                  | MSG_FLAG_STDERR     | MSG_FLAG_STDUI,
+};
+
+
+void    timestamp(char* buf, int buflen);
+char*   strconcat(const char*, const char*);
+
+
+int     msg_log(int type, const char* format, ...);
+void    msg_log_set_message_cb(msg_log_cb);
+
+bool    msg_log_get_notification_state(void);
+void    msg_log_reset_notification_state(void);
+
+#endif
diff --git a/libphin/AUTHORS b/libphin/AUTHORS
new file mode 100644
index 0000000..ac9116d
--- /dev/null
+++ b/libphin/AUTHORS
@@ -0,0 +1,12 @@
+-*- text -*-
+
+Thorsten Wilms <t_w_ at freenet.de>
+
+Pete Bessman <ninjadroid at gazuga.net>
+
+Loki Davison <loki.davison at gmail.com>
+
+Nedko Arnaudov <nedko at arnaudov.name>
+
+James W. Morris <james at jwm-art.net>
+
diff --git a/libphin/BUGS b/libphin/BUGS
new file mode 100644
index 0000000..35a960b
--- /dev/null
+++ b/libphin/BUGS
@@ -0,0 +1,4 @@
+-*- text -*-
+
+* Grab a fanslider and wiggle back and forth, sometimes the fan will
+  "lock up" and do funny things...
diff --git a/libphin/CMakeLists.txt b/libphin/CMakeLists.txt
new file mode 100644
index 0000000..1db196d
--- /dev/null
+++ b/libphin/CMakeLists.txt
@@ -0,0 +1,21 @@
+file (GLOB LIBPHIN_SOURCES *.c)
+
+include_directories (
+    ${GTK2_INCLUDE_DIRS}
+    ${LIBGNOMECANVAS2_INCLUDE_DIRS}
+    )
+
+link_directories (
+    ${GTK2_LIBRARY_DIRS}
+    ${LIBGNOMECANVAS2_LIBRARY_DIRS}
+    )
+
+add_definitions (
+    ${GTK2_CFLAGS_OTHER}
+    ${LIBGNOMECANVAS2_CFLAGS_OTHER}
+    )
+
+add_library( phin STATIC ${LIBPHIN_SOURCES})
+SET(CMAKE_INSTALL_LIBDIR lib CACHE PATH "Output directory for libraries")
+
+target_link_Libraries(phin ${GTK2_LIBRARIES} ${LIBGNOMECANVAS2_LIBRARIES})
diff --git a/COPYING b/libphin/COPYING
similarity index 100%
copy from COPYING
copy to libphin/COPYING
diff --git a/libphin/README b/libphin/README
new file mode 100644
index 0000000..fcb4cdb
--- /dev/null
+++ b/libphin/README
@@ -0,0 +1,6 @@
+PHIN is PHAT without the phatpad or phatknob
+and without deprecated GTK/GDK code
+so it won't die just yet
+and neither will petri-foo.
+ha.
+
diff --git a/libphin/TODO b/libphin/TODO
new file mode 100644
index 0000000..2903d4b
--- /dev/null
+++ b/libphin/TODO
@@ -0,0 +1,32 @@
+-*- text -*-
+
+---must---
+
+* format specifier thing for sliderbuttons is LAME ASS!  Just use a
+  num digits arg.
+
+---gravy---
+
+* add escaping to fansliders
+
+* add ghosting to fansliders
+
+* add support for update policies
+
+* implement properties and style properties
+
+* add default context menu (copy, paste, revert, previous);
+  perhaps use interfaces?
+
+* use gconf to allow the user to set properties accross all
+  applications, create a config frontend
+
+* add option to turn off fan drawing and mouse hiding/warping for
+  fansliders
+
+* add fan threshold to fansliders
+
+---magic---
+
+* figure out how to make fanslider fans not flicker when pulled out to
+  the left or up (thresholds might help)
diff --git a/libphin/phin.h b/libphin/phin.h
new file mode 100644
index 0000000..29d7c2f
--- /dev/null
+++ b/libphin/phin.h
@@ -0,0 +1,35 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_H__
+#define __PHIN_H__
+
+#include <phinvfanslider.h>
+#include <phinhfanslider.h>
+#include <phinfanslider.h>
+#include <phinsliderbutton.h>
+#include <phinkeyboard.h>
+#include <phinvkeyboard.h>
+#include <phinhkeyboard.h>
+
+#endif /* __PHIN_H__ */
diff --git a/libphin/phinfanslider.c b/libphin/phinfanslider.c
new file mode 100644
index 0000000..3dfacbc
--- /dev/null
+++ b/libphin/phinfanslider.c
@@ -0,0 +1,2073 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <math.h>
+#include "phinprivate.h"
+#include "phinfanslider.h"
+
+/* magic numbers */
+enum
+{
+    FAN_RISE = 3,
+    FAN_RUN = 1,
+    SLIDER_WIDTH = 16,
+    SLIDER_LENGTH = 32,
+    THRESHOLD = 4,
+};
+
+/* states */
+enum
+{
+    STATE_NORMAL,
+    STATE_CLICKED,
+    STATE_SCROLL,
+};
+
+#define VALUE_RATIO_NORMAL  1.00
+#define VALUE_RATIO_SHIFT   0.10
+#define VALUE_RATIO_CTRL    0.01
+
+
+
+/* signals */
+enum
+{
+    VALUE_CHANGED_SIGNAL,
+    CHANGED_SIGNAL,
+    LAST_SIGNAL,
+};
+
+static int signals[LAST_SIGNAL];
+
+
+typedef struct _PhinFanSliderPrivate PhinFanSliderPrivate;
+
+#define PHIN_FAN_SLIDER_GET_PRIVATE(obj) \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), PHIN_TYPE_FAN_SLIDER, \
+                                        PhinFanSliderPrivate))
+
+struct _PhinFanSliderPrivate
+{
+    GtkAdjustment* adjustment;
+    GtkAdjustment* adjustment_prv;
+    double         val;
+    double         center_val;
+    int            xclick_root;
+    int            yclick_root;
+    int            xclick;
+    int            yclick;
+    double         value_update_ratio;
+    int            fan_max_thickness;
+    int            state;
+    gboolean       inverted;
+    int            direction;
+    gboolean       is_log;
+    GtkOrientation orientation;
+    GtkWidget*     fan_window;
+    GdkCursor*     arrow_cursor;
+    GdkCursor*     empty_cursor;
+    GdkWindow*     event_window;
+    GtkWidget*     hint_window0;
+    GtkWidget*     hint_window1;
+    GdkRectangle   cur_fan;
+    gboolean       use_default_value;
+    gdouble        default_value;
+};
+
+
+/* forward declarations */
+G_DEFINE_TYPE(PhinFanSlider, phin_fan_slider, GTK_TYPE_WIDGET);
+
+
+static gboolean fans_active =       FALSE;
+static int      fan_max_height =    0;
+static int      fan_max_width  =    0;
+
+
+static void phin_fan_slider_dispose         (GObject* object);
+static void phin_fan_slider_realize         (GtkWidget* widget);
+static void phin_fan_slider_unrealize       (GtkWidget *widget);
+static void phin_fan_slider_map             (GtkWidget *widget);
+static void phin_fan_slider_unmap           (GtkWidget *widget);
+
+static void phin_fan_slider_draw_fan        (PhinFanSlider* slider);
+static int  phin_fan_slider_get_fan_length  (PhinFanSlider* slider);
+static void phin_fan_slider_update_hints    (PhinFanSlider* slider);
+
+static void phin_fan_slider_size_request    (GtkWidget*widget,
+                                            GtkRequisition* requisition);
+
+static void phin_fan_slider_size_allocate   (GtkWidget* widget,
+                                            GtkAllocation* allocation);
+
+static gboolean phin_fan_slider_expose      (GtkWidget* widget,
+                                            GdkEventExpose* event);
+
+static gboolean phin_fan_slider_button_press(GtkWidget* widget,
+                                            GdkEventButton* event);
+
+static gboolean phin_fan_slider_button_release  (GtkWidget* widget,
+                                                GdkEventButton* event);
+
+static gboolean phin_fan_slider_key_press   (GtkWidget* widget,
+                                            GdkEventKey* event);
+
+static gboolean phin_fan_slider_scroll      (GtkWidget* widget,
+                                            GdkEventScroll* event);
+
+static gboolean phin_fan_slider_motion_notify   (GtkWidget* widget,
+                                                GdkEventMotion* event);
+
+static gboolean phin_fan_slider_enter_notify    (GtkWidget* widget,
+                                                GdkEventCrossing* event);
+
+static gboolean phin_fan_slider_leave_notify    (GtkWidget* widget,
+                                                GdkEventCrossing* event);
+
+static void phin_fan_slider_calc_layout     (PhinFanSlider* slider,
+                                            int* x, int* y, int* w, int* h);
+
+static void phin_fan_slider_update_value    (PhinFanSlider* slider,
+                                            int x_root, int y_root);
+
+static void phin_fan_slider_update_fan      (PhinFanSlider* slider,
+                                            int x, int y);
+
+static gboolean phin_fan_slider_fan_expose (GtkWidget*      widget,
+                                            GdkEventExpose* event,
+                                            PhinFanSlider*  slider);
+
+static void phin_fan_slider_fan_show (GtkWidget* widget,
+                                      GtkWidget* slider);
+
+static gboolean phin_fan_slider_hint_expose (GtkWidget* widget,
+                                             GdkEventExpose* event,
+                                             GtkWidget* slider);
+
+static void phin_fan_slider_adjustment_changed (GtkAdjustment* adjustment,
+                                                PhinFanSlider* slider);
+
+static void phin_fan_slider_adjustment_value_changed(   GtkAdjustment*,
+                                                        PhinFanSlider*);
+
+
+void phin_fan_slider_set_fans_active(gboolean enable_fans)
+{
+    fans_active = enable_fans;
+}
+
+gboolean phin_fan_slider_get_fans_active(void)
+{
+    return fans_active;
+}
+
+
+/**
+ * phin_fan_slider_set_value:
+ * @slider: a #PhinFanSlider
+ * @value: a new value for the slider
+ * 
+ * Sets the current value of the slider.  If the value is outside the
+ * range of values allowed by @slider, it will be clamped to fit
+ * within them.  The slider emits the "value-changed" signal if the
+ * value changes.
+ *
+ */
+void phin_fan_slider_set_value (PhinFanSlider* slider, double value)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    gdouble lower = gtk_adjustment_get_lower(p->adjustment);
+    gdouble upper = gtk_adjustment_get_upper(p->adjustment);
+
+    value = CLAMP (value, lower, upper);
+
+    gtk_adjustment_set_value (p->adjustment, value);
+
+    if(p->is_log)
+    {
+        gtk_adjustment_set_value(GTK_ADJUSTMENT(p->adjustment_prv),
+                            log(value - lower) / log(upper - lower));
+    }
+    else
+    {
+        gtk_adjustment_set_value(GTK_ADJUSTMENT(p->adjustment_prv),
+                            (value - lower) / (upper - lower));
+    }
+}
+
+
+void phin_fan_slider_set_log (PhinFanSlider* slider, gboolean is_log)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+    p->is_log = is_log;
+}
+
+
+gboolean phin_fan_slider_is_log (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+    return p->is_log;
+}
+
+
+/**
+ * phin_fan_slider_get_value:
+ * @slider: a #PhinFanSlider
+ *
+ * Retrieves the current value of the slider.
+ *
+ * Returns: current value of the slider.
+ *
+ */
+double phin_fan_slider_get_value (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    gdouble lower = gtk_adjustment_get_lower(p->adjustment);
+    gdouble upper = gtk_adjustment_get_upper(p->adjustment);
+    gdouble value = 0;
+    gdouble prv_value = gtk_adjustment_get_value(p->adjustment_prv);
+
+    if(p->is_log)
+    {   /* FIXME: log scale */
+        value = exp(prv_value * log(upper - lower)) + lower;
+    }
+    else
+    {
+        value = prv_value * (upper - lower) + lower;
+    }
+
+    /* not sure what the purpose of this set value call is: */
+    gtk_adjustment_set_value(p->adjustment, value);
+
+    return value;
+}
+
+/*
+http://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249
+  function logslider(value) {
+  // value will be between 0 and 100
+  var slidermin = 0;
+  var slidermax = 100;
+
+  // The result should be between 100 an 10000000
+  var minv = Math.log(100);
+  var maxv = Math.log(10000000);
+
+  // calculate adjustment factor
+  var scale = (maxv-minv) / (slidermax-slidermin);
+
+  return Math.exp(minv + scale*(value-slidermin));
+}
+
+*/
+/**
+ * phin_fan_slider_set_range:
+ * @slider: a #PhinFanSlider
+ * @lower: lowest allowable value
+ * @upper: highest allowable value
+ * 
+ * Sets the range of allowable values for the slider, and  clamps the slider's
+ * current value to be between @lower and @upper.
+ *
+ */
+void phin_fan_slider_set_range (PhinFanSlider* slider,
+                                double lower, double upper)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+    double value;
+
+    g_return_if_fail (lower <= upper);
+
+    gtk_adjustment_set_lower(p->adjustment, lower);
+    gtk_adjustment_set_upper(p->adjustment, upper);
+
+    value = CLAMP(gtk_adjustment_get_value(p->adjustment), lower, upper);
+
+    // XXX not sure about these
+    gtk_adjustment_changed(p->adjustment);
+    gtk_adjustment_set_value(p->adjustment, value);
+}
+
+/**
+ * phin_fan_slider_get_range:
+ * @slider: a #PhinFanSlider
+ * @lower: retrieves lowest allowable value
+ * @upper: retrieves highest allowable value
+ *
+ * Places the range of allowable values for @slider into @lower
+ * and @upper.  Either variable may be set to %NULL if you are not
+ * interested in its value.
+ *
+ */
+void phin_fan_slider_get_range (PhinFanSlider* slider,
+                                double* lower, double* upper)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    if (lower)
+        *lower = gtk_adjustment_get_lower(p->adjustment);
+    if (upper)
+        *upper = gtk_adjustment_get_upper(p->adjustment);
+}
+
+/**
+ * phin_fan_slider_set_adjustment:
+ * @slider: a #PhinFanSlider
+ * @adjustment: a #GtkAdjustment
+ *
+ * Sets the adjustment used by @slider.  Every #PhinFanSlider uses an
+ * adjustment to store its current value and its range of allowable
+ * values.  If @adjustment is %NULL, a new adjustment with a value of
+ * zero and a range of [-1.0, 1.0] will be created.
+ *
+ */
+void phin_fan_slider_set_adjustment (PhinFanSlider* slider,
+                                     GtkAdjustment* adjustment)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    g_return_if_fail (p->adjustment != adjustment);
+
+    if (!adjustment)
+        adjustment = (GtkAdjustment*)
+                        gtk_adjustment_new (0.0, -1.0, 1.0, 1.0, 1.0, 0.0);
+    else
+        g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+
+    if (p->adjustment)
+    {
+        g_signal_handlers_disconnect_by_func(
+                                p->adjustment,
+                                phin_fan_slider_adjustment_changed,
+                                (gpointer)slider);
+
+        g_signal_handlers_disconnect_by_func(
+                                p->adjustment,
+                                phin_fan_slider_adjustment_value_changed,
+                                (gpointer)slider);
+
+        g_object_unref (p->adjustment);
+    }
+
+    p->adjustment = adjustment;
+    g_object_ref (adjustment);
+    g_object_ref_sink (GTK_OBJECT (adjustment));
+
+    phin_fan_slider_adjustment_changed(p->adjustment, slider);
+
+    phin_fan_slider_set_value(PHIN_FAN_SLIDER (slider),
+                                gtk_adjustment_get_value(adjustment));
+}
+
+/**
+ * phin_fan_slider_get_adjustment:
+ * @slider: a #PhinFanSlider
+ *
+ * Retrives the current adjustment in use by @slider.
+ *
+ * Returns: @slider's current #GtkAdjustment
+ *
+ */
+GtkAdjustment* phin_fan_slider_get_adjustment (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    /* I can't imagine this ever being true, but just to be
+     * "safe" ... */
+    if (!p->adjustment)
+        phin_fan_slider_set_adjustment (slider, NULL);
+
+    return p->adjustment;
+}
+
+/**
+ * phin_fan_slider_set_inverted:
+ * @slider: a #PhinFanSlider
+ * @inverted: %TRUE to invert the fanslider
+ *  
+ * Sets in which direction the fanslider should draw increasing
+ * values.  By default, horizontal fansliders draw low to high from
+ * left to right, and vertical fansliders draw from bottom to top.
+ * You can reverse this behavior by setting @inverted to %TRUE.
+ * 
+ */
+void phin_fan_slider_set_inverted (PhinFanSlider* slider, gboolean inverted)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    p->inverted = inverted;
+    gtk_widget_queue_draw (GTK_WIDGET (slider));
+}
+
+/**
+ * phin_fan_slider_get_inverted:
+ * @slider: a #PhinFanSlider
+ *
+ * Determines whether @slider is inverted or not.
+ *
+ * Returns: %TRUE if @slider is inverted
+ *
+ */
+gboolean phin_fan_slider_get_inverted (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+    return p->inverted;
+}
+
+/**
+ * phin_fan_slider_set_default_value:
+ * @slider: a #PhinFanSlider
+ * @value: the default value
+ *  
+ * Set default value of the slider. Slider is reset to this value
+ * when middle mouse button is pressed.
+ */
+void phin_fan_slider_set_default_value(PhinFanSlider* slider, gdouble value)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    p->use_default_value = TRUE;
+    p->default_value = value;
+}
+
+void phin_fan_slider_set_orientation(PhinFanSlider* slider,
+                                     GtkOrientation o)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+    p->orientation = o;
+}
+
+
+static void phin_fan_slider_class_init (PhinFanSliderClass* klass)
+{
+    GObjectClass* object_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+    phin_fan_slider_parent_class = g_type_class_peek_parent(klass);
+    GdkScreen*      screen       = gdk_screen_get_default();
+
+    debug ("class init\n");
+
+    phin_fan_slider_parent_class = g_type_class_peek(gtk_widget_get_type());
+
+    object_class->dispose =                 phin_fan_slider_dispose;
+    widget_class->realize =                 phin_fan_slider_realize;
+    widget_class->unrealize =               phin_fan_slider_unrealize;
+    widget_class->map =                     phin_fan_slider_map;
+    widget_class->unmap =                   phin_fan_slider_unmap;
+    widget_class->expose_event =            phin_fan_slider_expose;
+    widget_class->size_request =            phin_fan_slider_size_request;
+    widget_class->size_allocate =           phin_fan_slider_size_allocate;
+    widget_class->button_press_event =      phin_fan_slider_button_press;
+    widget_class->button_release_event =    phin_fan_slider_button_release;
+    widget_class->key_press_event =         phin_fan_slider_key_press;
+    widget_class->scroll_event =            phin_fan_slider_scroll;
+    widget_class->motion_notify_event =     phin_fan_slider_motion_notify;
+    widget_class->enter_notify_event =      phin_fan_slider_enter_notify;
+    widget_class->leave_notify_event =      phin_fan_slider_leave_notify;
+
+    /**
+     * PhinFanSlider::value-changed:
+     * @slider: the object on which the signal was emitted
+     *
+     * The "value-changed" signal is emitted when the value of the
+     * slider's adjustment changes.
+     *
+     */
+    signals[VALUE_CHANGED_SIGNAL] =
+        g_signal_new ("value-changed",
+                      G_TYPE_FROM_CLASS(klass),
+                      G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET(PhinFanSliderClass, value_changed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    /**
+     * PhinFanSlider::changed:
+     * @slider: the object on which the signal was emitted
+     *
+     * The "changed" signal is emitted when any parameter of the
+     * slider's adjustment changes, except for the %value parameter.
+     *
+     */
+    signals[CHANGED_SIGNAL] =
+        g_signal_new ("changed",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (PhinFanSliderClass, changed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    klass->value_changed = NULL;
+    klass->changed = NULL;
+
+    if (screen)
+        fan_max_width = gdk_screen_get_width (screen);
+    else
+        fan_max_width = 1280;
+
+    if (screen)
+        fan_max_height = gdk_screen_get_height (screen);
+    else
+        fan_max_height = 1024;
+
+
+    g_type_class_add_private(object_class, sizeof(PhinFanSliderPrivate));
+}
+
+
+static void phin_fan_slider_init (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(slider);
+
+    gtk_widget_set_has_window(GTK_WIDGET(slider), FALSE);
+    gtk_widget_set_can_focus(GTK_WIDGET(slider), TRUE);
+
+    debug ("init\n");
+
+    p->adjustment = NULL;
+    p->adjustment_prv = (GtkAdjustment*)
+                        gtk_adjustment_new (0.0, 0.0, 1.0, 0.1, 0.1, 0.0);
+    p->val = 0.69;
+    p->center_val = -1;
+    p->xclick_root = 0;
+    p->yclick_root = 0;
+    p->xclick = 0;
+    p->yclick = 0;
+    p->value_update_ratio = VALUE_RATIO_NORMAL;
+    p->fan_max_thickness = 1;
+    p->inverted = FALSE;
+    p->direction = 0;
+    p->state = STATE_NORMAL;
+    p->orientation = GTK_ORIENTATION_HORIZONTAL;
+    p->fan_window = NULL;
+    p->arrow_cursor = NULL;
+    p->empty_cursor = NULL;
+    p->event_window = NULL;
+    p->hint_window0 = NULL;
+    p->hint_window1 = NULL;
+    p->is_log = 0;
+    p->use_default_value = FALSE;
+
+    g_signal_connect (p->adjustment_prv, "changed",
+                      G_CALLBACK (phin_fan_slider_adjustment_changed),
+                      (gpointer) slider);
+    g_signal_connect (p->adjustment_prv, "value_changed",
+                      G_CALLBACK (phin_fan_slider_adjustment_value_changed),
+                      (gpointer) slider);
+
+    phin_fan_slider_adjustment_changed (p->adjustment_prv, slider);
+    phin_fan_slider_adjustment_value_changed (p->adjustment_prv, slider);
+
+}
+
+
+static void phin_fan_slider_dispose (GObject* object)
+{
+    PhinFanSlider* slider;
+    PhinFanSliderPrivate* p;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (object));
+
+    slider = PHIN_FAN_SLIDER(object);
+    p = PHIN_FAN_SLIDER_GET_PRIVATE(object);
+
+    debug ("dispose %p\n", object);
+
+    if (p->arrow_cursor != NULL)
+    {
+        gdk_cursor_unref (p->arrow_cursor);
+        p->arrow_cursor = NULL;
+    }
+
+    if (p->empty_cursor != NULL)
+    {
+        gdk_cursor_unref (p->empty_cursor);
+        p->empty_cursor = NULL;
+    }
+
+    if (p->event_window != NULL)
+    {
+        gdk_window_destroy (p->event_window);
+        p->event_window = NULL;
+    }
+
+    if (p->fan_window != NULL)
+    {
+        gtk_widget_destroy (p->fan_window);
+        p->fan_window = NULL;
+    }
+
+    if (p->hint_window0 != NULL)
+    {
+        gtk_widget_destroy (p->hint_window0);
+        p->hint_window0 = NULL;
+    }
+
+    if (p->hint_window1 != NULL)
+    {
+        gtk_widget_destroy (p->hint_window1);
+        p->hint_window1 = NULL;
+    }
+
+    if (p->adjustment)
+    {
+        g_signal_handlers_disconnect_by_func(
+                                p->adjustment,
+                                phin_fan_slider_adjustment_changed,
+                                (gpointer)slider);
+
+        g_signal_handlers_disconnect_by_func(
+                                p->adjustment,
+                                phin_fan_slider_adjustment_value_changed,
+                                (gpointer)slider);
+
+        /* called ref on it so we must call unref */
+        g_object_unref (p->adjustment);
+        p->adjustment = NULL;
+    }
+
+    if (p->adjustment_prv)
+    {
+        g_signal_handlers_disconnect_by_func(
+                                p->adjustment_prv,
+                                phin_fan_slider_adjustment_changed,
+                                (gpointer)slider);
+
+        g_signal_handlers_disconnect_by_func(
+                                p->adjustment_prv,
+                                phin_fan_slider_adjustment_value_changed,
+                                (gpointer)slider);
+    }
+}
+
+
+static void phin_fan_slider_realize (GtkWidget* widget)
+{
+    PhinFanSlider* slider;
+    PhinFanSliderPrivate* p;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+    GdkWindow* window;
+    GtkStyle* style;
+    debug ("realize\n");
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (widget));
+
+    gtk_widget_set_realized(GTK_WIDGET(widget), TRUE);
+
+    slider = PHIN_FAN_SLIDER(widget);
+    p = PHIN_FAN_SLIDER_GET_PRIVATE(widget);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        p->arrow_cursor = gdk_cursor_new (GDK_SB_V_DOUBLE_ARROW);
+    }
+    else
+    {
+        p->arrow_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+    }
+
+    p->empty_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
+
+    window = gtk_widget_get_parent_window (widget);
+    gtk_widget_set_window(widget, window);
+    g_object_ref (window);
+
+    style = gtk_widget_get_style(widget);
+    style = gtk_style_attach(style, window);
+    gtk_widget_set_style(widget, style);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.wclass      = GDK_INPUT_ONLY;
+    attributes.event_mask  = gtk_widget_get_events (widget);
+    attributes.event_mask |= (GDK_BUTTON_PRESS_MASK
+                              | GDK_BUTTON_RELEASE_MASK
+                              | GDK_POINTER_MOTION_MASK
+                              | GDK_POINTER_MOTION_HINT_MASK
+                              | GDK_ENTER_NOTIFY_MASK
+                              | GDK_LEAVE_NOTIFY_MASK
+                              | GDK_SCROLL_MASK);
+    phin_fan_slider_calc_layout (slider,
+                                 &attributes.x,
+                                 &attributes.y,
+                                 &attributes.width,
+                                 &attributes.height);
+    attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+    p->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                           &attributes, attributes_mask);
+    gdk_window_set_user_data (p->event_window, widget);
+    gdk_window_set_cursor (p->event_window, p->arrow_cursor);
+
+    p->fan_window = gtk_window_new (GTK_WINDOW_POPUP);
+
+    gtk_window_resize(GTK_WINDOW(p->fan_window),
+                                fan_max_width, fan_max_height);
+
+    gtk_widget_set_app_paintable (p->fan_window, TRUE);
+
+    g_signal_connect (G_OBJECT (p->fan_window),
+                      "expose-event",
+                      G_CALLBACK (phin_fan_slider_fan_expose),
+                      (gpointer) slider);
+
+    g_signal_connect (G_OBJECT (p->fan_window),
+                      "show",
+                      G_CALLBACK (phin_fan_slider_fan_show),
+                      (gpointer) slider);
+
+    g_signal_connect (G_OBJECT(p->fan_window),
+                      "screen-changed",
+                      G_CALLBACK (phin_screen_changed), NULL);
+
+    phin_screen_changed(p->fan_window, NULL, NULL);
+
+    p->hint_window0 = gtk_window_new (GTK_WINDOW_POPUP);
+    gtk_widget_realize (p->hint_window0);
+    g_signal_connect (G_OBJECT (p->hint_window0),
+                      "expose-event",
+                      G_CALLBACK (phin_fan_slider_hint_expose),
+                      (gpointer) slider);
+
+    p->hint_window1 = gtk_window_new (GTK_WINDOW_POPUP);
+    gtk_widget_realize (p->hint_window1);
+    g_signal_connect (G_OBJECT (p->hint_window1),
+                      "expose-event",
+                      G_CALLBACK (phin_fan_slider_hint_expose),
+                      (gpointer) slider);
+
+    /* a priming call */
+    phin_fan_slider_update_hints (slider);
+}
+
+static void phin_fan_slider_unrealize (GtkWidget *widget)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);;
+    GtkWidgetClass* klass = GTK_WIDGET_CLASS(phin_fan_slider_parent_class);
+
+    debug ("unrealize\n");
+
+    gdk_cursor_unref (p->arrow_cursor);
+    p->arrow_cursor = NULL;
+
+    gdk_cursor_unref (p->empty_cursor);
+    p->empty_cursor = NULL;
+
+    gdk_window_set_user_data (p->event_window, NULL);
+    gdk_window_destroy (p->event_window);
+    p->event_window = NULL;
+
+    gtk_widget_destroy (p->fan_window);
+    p->fan_window = NULL;
+
+    gtk_widget_destroy (p->hint_window0);
+    p->hint_window0 = NULL;
+
+    gtk_widget_destroy (p->hint_window1);
+    p->hint_window1 = NULL;
+
+    if (klass->unrealize)
+        klass->unrealize (widget);
+}
+
+
+static void phin_fan_slider_map (GtkWidget *widget)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+
+    debug ("map\n");
+
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (widget));
+
+    gdk_window_show (p->event_window);
+
+    GTK_WIDGET_CLASS(phin_fan_slider_parent_class)->map(widget);
+}
+
+
+static void phin_fan_slider_unmap (GtkWidget *widget)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE(widget);;
+
+    debug ("unmap\n");
+
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (widget));
+
+    gdk_window_hide (p->event_window);
+
+    GTK_WIDGET_CLASS(phin_fan_slider_parent_class)->unmap(widget);
+}
+
+static void phin_fan_slider_size_request (GtkWidget*      widget,
+                                          GtkRequisition* requisition)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);;
+    int focus_width, focus_pad;
+    int pad;
+
+    debug ("size request\n");
+
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (widget));
+
+    gtk_widget_style_get (widget,
+                          "focus-line-width", &focus_width,
+                          "focus-padding", &focus_pad,
+                          NULL);
+
+    pad = 2 * (focus_width + focus_pad);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        requisition->width = SLIDER_WIDTH + pad;
+        requisition->height = SLIDER_LENGTH + pad;
+    }
+    else
+    {
+        requisition->width = SLIDER_LENGTH + pad;
+        requisition->height = SLIDER_WIDTH + pad;
+    }
+}
+
+static void phin_fan_slider_size_allocate (GtkWidget*     widget,
+                                           GtkAllocation* allocation)
+{
+    PhinFanSlider* slider = PHIN_FAN_SLIDER (widget);
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);;
+    int x, y;
+    int w, h;
+
+    debug ("size allocate\n");
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (widget));
+    g_return_if_fail (allocation != NULL);
+
+    gtk_widget_set_allocation(widget, allocation);
+
+    phin_fan_slider_calc_layout (slider, &x, &y, &w, &h);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        p->fan_max_thickness = ((fan_max_height - h)
+                                     / (2 * FAN_RISE / FAN_RUN));
+    }
+    else
+    {
+        p->fan_max_thickness = ((fan_max_width - w)
+                                     / (2 * FAN_RISE / FAN_RUN));
+    }
+
+    if (gtk_widget_get_realized(widget))
+    {
+        gdk_window_move_resize (p->event_window,
+                                x, y, w, h);
+    }
+}
+
+
+static void draw_fan_rectangle(cairo_t* cr, double x, double y,
+                                            double w, double h,
+                                            GdkColor* col)
+{
+    gdk_cairo_set_source_color(cr, col);
+    cairo_set_line_width(cr, 1.0);
+    cairo_rectangle(cr, x, y, w, h);
+    cairo_stroke_preserve(cr);
+    cairo_fill(cr);
+}
+
+
+static gboolean phin_fan_slider_expose (GtkWidget*      widget,
+                                        GdkEventExpose* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+    PhinFanSlider* slider;
+    int x, y;
+    int w, h;
+    int fx, fy;                /* "filled" coordinates */
+    int fw, fh;
+
+    GtkStyle* style;
+    cairo_t* cr;
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (PHIN_IS_FAN_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    if (event->count > 0)
+        return FALSE;
+
+    slider = (PhinFanSlider*) widget;
+
+    style = gtk_widget_get_style(widget);
+    cr = gdk_cairo_create(gtk_widget_get_window(widget));
+
+    phin_fan_slider_calc_layout (slider, &x, &y, &w, &h);
+     
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        if (p->center_val >= 0)
+        {
+            fw = w;
+            fh = ABS (p->val - p->center_val) * h;
+            fx = x;
+            fy = y + h - (p->center_val * h);
+
+            if ((p->val > p->center_val && !p->inverted)
+             || (p->val < p->center_val &&  p->inverted))
+            {
+                fy -= fh;
+            }
+
+        }
+        else
+        {
+            fw = w;
+            fh = p->val * h;
+            fx = x;
+            fy = (p->inverted)? y: y + h - fh;
+        }
+    }
+    else
+    {
+        if (p->center_val >= 0)
+        {
+            fw = ABS (p->val - p->center_val) * w;
+            fh = h;
+            fx = x + (p->center_val * w);
+            fy = y;
+
+            if ((p->val < p->center_val && !p->inverted)
+             || (p->val > p->center_val &&  p->inverted))
+            {
+                fx -= fw;
+            }
+        }
+        else
+        {
+            fw = p->val * w;
+            fh = h;
+            fx = (p->inverted)? x + w - fw: x;
+            fy = y;
+        }
+    }
+
+    if (!gtk_widget_is_sensitive(widget))
+    {
+        draw_fan_rectangle(cr, x, y, w, h,
+                                &style->dark[GTK_STATE_INSENSITIVE]);
+        draw_fan_rectangle(cr, fx, fy, fw, fh,
+                                &style->fg[GTK_STATE_INSENSITIVE]);
+    }
+    else
+    {
+        draw_fan_rectangle(cr, x, y, w, h,
+                                &style->dark[GTK_STATE_NORMAL]);
+        draw_fan_rectangle(cr, fx, fy, fw, fh,
+                                &style->base[GTK_STATE_SELECTED]);
+    }
+
+    cairo_destroy(cr);
+
+    if (gtk_widget_has_focus (widget))
+    {
+        int focus_width, focus_pad;
+        int pad;
+          
+        gtk_widget_style_get (widget,
+                              "focus-line-width", &focus_width,
+                              "focus-padding", &focus_pad,
+                              NULL);
+
+        pad = focus_width + focus_pad;
+
+        x -= pad;
+        y -= pad;
+        w += 2*pad;
+        h += 2*pad;
+
+        gtk_paint_focus (style, gtk_widget_get_window(widget),
+                                gtk_widget_get_state (widget),
+                                NULL, widget, NULL,
+                                x, y, w, h);
+    }
+
+    if (gtk_widget_get_visible (p->fan_window))
+        gtk_widget_queue_draw (p->fan_window);
+     
+    return FALSE;
+}
+
+
+static gboolean phin_fan_slider_button_press (GtkWidget*      widget,
+                                              GdkEventButton* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+    PhinFanSlider* slider;
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (PHIN_IS_FAN_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    slider = (PhinFanSlider*) widget;
+
+    if(event->button == 1)
+    {
+        gtk_widget_grab_focus (widget);
+         
+        if (p->state == STATE_SCROLL)
+        {
+            p->state = STATE_NORMAL;
+            gdk_window_set_cursor (p->event_window, p->arrow_cursor);
+            return FALSE;
+        }
+
+        gdk_window_set_cursor (p->event_window, p->empty_cursor);
+
+        p->xclick_root = event->x_root;
+        p->xclick = event->x;
+        p->yclick_root = event->y_root;
+        p->yclick = event->y;
+        p->state = STATE_CLICKED;
+    }
+    else if (event->button == 2 && p->use_default_value)
+    {
+        phin_fan_slider_set_value(slider, p->default_value);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+
+static gboolean phin_fan_slider_button_release (GtkWidget*      widget,
+                                                GdkEventButton* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (PHIN_IS_FAN_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    gdk_window_set_cursor (p->event_window, p->arrow_cursor);
+
+    if (p->state == STATE_CLICKED)
+    {
+        p->state = STATE_NORMAL;
+
+        phin_warp_pointer (event->x_root,
+                           event->y_root,
+                           p->xclick_root,
+                           p->yclick_root);
+
+        if (gtk_widget_get_visible (p->fan_window))
+            gtk_widget_hide (p->fan_window);
+
+        if (gtk_widget_get_visible (p->hint_window0))
+            gtk_widget_hide (p->hint_window0);
+
+        if (gtk_widget_get_visible (p->hint_window1))
+            gtk_widget_hide (p->hint_window1);
+    }
+     
+    return FALSE;
+}
+
+static gboolean phin_fan_slider_key_press (GtkWidget* widget,
+                                           GdkEventKey* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+    GtkAdjustment* adj = p->adjustment_prv;
+    gdouble inc;
+
+    debug ("key press\n");
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        switch (event->keyval)
+        {
+        case GDK_Up:
+            inc = gtk_adjustment_get_step_increment(adj);
+            break;
+        case GDK_Down:
+            inc = -gtk_adjustment_get_step_increment(adj);
+            break;
+        case GDK_Page_Up:
+            inc = gtk_adjustment_get_page_increment(adj);
+            break;
+        case GDK_Page_Down:
+            inc = -gtk_adjustment_get_page_increment(adj);
+            break;
+        default:
+            return FALSE;
+        }
+    }
+    else
+    {
+        switch (event->keyval)
+        {
+        case GDK_Right:
+            inc = gtk_adjustment_get_step_increment(adj);
+            break;
+        case GDK_Left:
+            inc = -gtk_adjustment_get_step_increment(adj);
+            break;
+        case GDK_Page_Up:
+            inc = gtk_adjustment_get_page_increment(adj);
+            break;
+        case GDK_Page_Down:
+            inc = -gtk_adjustment_get_page_increment(adj);
+            break;
+        default:
+            return FALSE;
+        }
+    }
+
+    if (p->inverted)
+        inc = -inc;
+     
+    gtk_adjustment_set_value (adj, gtk_adjustment_get_value(adj) + inc);
+
+    return TRUE;
+}
+
+static gboolean phin_fan_slider_scroll (GtkWidget* widget,
+                                        GdkEventScroll* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+
+    gdouble val, pinc;
+
+    gtk_widget_grab_focus (widget);
+
+    p->state = STATE_SCROLL;
+
+    p->xclick_root = event->x_root;
+    p->yclick_root = event->y_root;
+    p->xclick = event->x;
+    p->yclick = event->y;
+
+    gdk_window_set_cursor (p->event_window, p->empty_cursor);
+
+    val = gtk_adjustment_get_value(p->adjustment_prv);
+    pinc = gtk_adjustment_get_page_increment(p->adjustment_prv);
+
+    if (((event->direction == GDK_SCROLL_UP
+       || event->direction == GDK_SCROLL_RIGHT) && !p->inverted)
+     || ((event->direction == GDK_SCROLL_DOWN
+       || event->direction == GDK_SCROLL_LEFT) && p->inverted))
+    {
+        gtk_adjustment_set_value (p->adjustment_prv, val + pinc);
+    }
+    else
+    {
+        gtk_adjustment_set_value (p->adjustment_prv, val - pinc);
+    }
+
+    return TRUE;
+}
+
+/* ctrl locks precision, xshiftxlocksxvaluex shift increases precision */
+static gboolean phin_fan_slider_motion_notify (GtkWidget*      widget,
+                                               GdkEventMotion* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+    PhinFanSlider* slider;
+
+    GtkAllocation fan_alloc;
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (PHIN_IS_FAN_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    slider = (PhinFanSlider*) widget;
+    gtk_widget_get_allocation(p->fan_window, &fan_alloc);
+
+    switch (p->state)
+    {
+    case STATE_SCROLL:
+        if (ABS (event->x - p->xclick) >= THRESHOLD
+         || ABS (event->y - p->yclick) >= THRESHOLD)
+        {
+            gdk_window_set_cursor (p->event_window, p->arrow_cursor);
+            p->state = STATE_NORMAL;
+        }
+    case STATE_NORMAL:
+        goto skip;
+    }
+
+    if (fans_active && !(event->state & GDK_CONTROL_MASK))
+        phin_fan_slider_update_fan (slider, event->x, event->y);
+
+/*
+    if (!(event->state & GDK_SHIFT_MASK))
+        phin_fan_slider_update_value (slider, event->x_root, event->y_root);
+*/
+
+
+    /* shift no longer locks value */
+    if ((event->state & GDK_SHIFT_MASK))
+        p->value_update_ratio = VALUE_RATIO_SHIFT;
+    else if ((event->state & GDK_CONTROL_MASK))
+        p->value_update_ratio = VALUE_RATIO_CTRL;
+    else
+        p->value_update_ratio = VALUE_RATIO_NORMAL;
+
+    phin_fan_slider_update_value (slider, event->x_root, event->y_root);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        int destx = event->x_root;
+        int width;
+
+        gdk_window_get_geometry (p->event_window,
+                                 NULL, NULL, &width, NULL, NULL);
+
+        if (gtk_widget_get_visible (p->fan_window))
+        {
+            if (event->x_root > p->xclick_root)
+            {
+                if (event->state & GDK_CONTROL_MASK)
+                {
+                    destx = (p->xclick_root
+                             + width
+                             - p->xclick
+                             + fan_alloc.width);
+                }
+                else
+                {
+                    destx = MIN (event->x_root,
+                                 p->xclick_root
+                                 + width
+                                 - p->xclick
+                                 + fan_alloc.width);
+                }
+            }
+            else
+            {
+                if (event->state & GDK_CONTROL_MASK)
+                {
+                    destx = (p->xclick_root
+                             - p->xclick
+                             - fan_alloc.width);
+                }
+                else
+                {
+                    destx = MAX (event->x_root,
+                                 p->xclick_root
+                                 - p->xclick
+                                 - fan_alloc.width);
+                }
+            }
+        }
+        else if (event->state & GDK_CONTROL_MASK)
+        {
+            destx = p->xclick_root;
+        }
+
+        phin_warp_pointer (event->x_root,
+                           event->y_root,
+                           destx,
+                           p->yclick_root);
+    }
+    else
+    {
+        int desty = event->y_root;
+        int height;
+
+        gdk_window_get_geometry (p->event_window,
+                                 NULL, NULL, NULL, &height, NULL);
+
+        if (gtk_widget_get_visible (p->fan_window))
+        {
+            if (event->y_root > p->yclick_root)
+            {
+                if (event->state & GDK_CONTROL_MASK)
+                {
+                    desty = (p->yclick_root
+                             + height
+                             - p->yclick
+                             + fan_alloc.height);
+                }
+                else
+                {
+                    desty = MIN (event->y_root,
+                                 p->yclick_root
+                                 + height
+                                 - p->yclick
+                                 + fan_alloc.height);
+                }
+            }
+            else
+            {
+                if (event->state & GDK_CONTROL_MASK)
+                {
+                    desty = (p->yclick_root
+                             - p->yclick
+                             - fan_alloc.height);
+                }
+                else
+                {
+                    desty = MAX (event->y_root,
+                                 p->yclick_root
+                                 - p->yclick
+                                 - fan_alloc.height);
+                }
+            }
+        }
+        else if (event->state & GDK_CONTROL_MASK)
+        {
+            desty = p->yclick_root;
+        }
+               
+        phin_warp_pointer (event->x_root,
+                           event->y_root,
+                           p->xclick_root,
+                           desty);
+    }
+
+    gtk_widget_queue_draw (widget);
+
+    /* necessary in case update_fan() doesn't get called */
+    if (gtk_widget_get_visible(p->fan_window))
+        gtk_widget_queue_draw (p->fan_window);
+
+skip:
+    /* signal that we want more motion events */
+    gdk_window_get_pointer (NULL, NULL, NULL, NULL);
+
+    return FALSE;
+}
+
+
+static gboolean phin_fan_slider_enter_notify (GtkWidget* widget,
+                                              GdkEventCrossing* event)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+    GtkAllocation win0_alloc;
+    GtkAllocation win1_alloc;
+
+    int width;
+    int height;
+    PhinFanSlider* slider = PHIN_FAN_SLIDER (widget);
+
+    if (p->state == STATE_NORMAL)
+        gdk_window_set_cursor (p->event_window, p->arrow_cursor);
+
+    phin_fan_slider_update_hints (slider);
+
+    gdk_window_get_geometry (p->event_window,
+                            NULL, NULL, &width, &height, NULL);
+
+    gtk_widget_get_allocation(p->hint_window0, &win0_alloc);
+    gtk_widget_get_allocation(p->hint_window1, &win1_alloc);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        gtk_window_move (GTK_WINDOW (p->hint_window0),
+                        event->x_root - event->x - win0_alloc.width,
+                        (event->y_root - event->y)
+                        + (height - win0_alloc.height) / 2);
+
+        gtk_window_move (GTK_WINDOW (p->hint_window1),
+                        event->x_root - event->x + width,
+                        (event->y_root - event->y)
+                        + (height - win1_alloc.height) / 2);
+    }
+    else
+    {
+        gtk_window_move (GTK_WINDOW (p->hint_window0),
+                        (event->x_root - event->x)
+                        + (width - win0_alloc.width) / 2,
+                        event->y_root - event->y - win0_alloc.height);
+
+        gtk_window_move (GTK_WINDOW (p->hint_window1),
+                        (event->x_root - event->x)
+                        + (width - win1_alloc.width) / 2,
+                        event->y_root - event->y + height);
+    }
+
+    return FALSE;
+}
+
+static gboolean phin_fan_slider_leave_notify (GtkWidget* widget,
+                                              GdkEventCrossing* event)
+{
+    (void)event;
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (widget);
+
+    if (p->state == STATE_SCROLL)
+    {
+        gdk_window_set_cursor (p->event_window, NULL);
+        p->state = STATE_NORMAL;
+    }
+
+    if (gtk_widget_get_visible (p->hint_window0))
+        gtk_widget_hide (p->hint_window0);
+
+    if (gtk_widget_get_visible (p->hint_window1))
+        gtk_widget_hide (p->hint_window1);
+
+    return FALSE;
+}
+
+
+/* helper to reduce copy & paste */
+static void
+fan_slider_draw(cairo_t* cr, GdkPixmap* bitmap, PhinFanSlider* slider,
+                    int x, int y, int w, int h, int length, gdouble value)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    int offset;
+
+    GtkWidget*  widget = GTK_WIDGET(slider);
+    GtkStyle*   style = (bitmap) ? NULL : gtk_widget_get_style(widget);
+
+    double r1, g1, b1, a1; /* slider bg */
+    double r2, g2, b2, a2; /* slider fg */
+
+    /*  length: (orientation dependent, orientation as slider length)
+            length = fan length, so dependant on size of fan */
+
+    /*  p->center_val: dependant on range of slider and if zero is
+        within the range.
+            == ratio in range 0.0 to 1.0 of where zero in the value range
+            appears within the slider
+                or
+            == -1 if zero is not within the value range
+    */
+
+    if (bitmap)
+    {
+        r1 = g1 = b1 = a1 = 0.0;
+        r2 = g2 = b2 = a2 = 1.0;
+    }
+    else
+    {
+        gdk_col_to_double(&style->dark[GTK_STATE_NORMAL], &r1, &g1, &b1);
+        gdk_col_to_double(&style->base[GTK_STATE_SELECTED], &r2, &g2, &b2);
+        a1 = 0.35;
+        a2 = 0.75;
+    }
+
+    cairo_set_source_rgba (cr, 0, 0, 0, 0);
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_paint (cr);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        int sign_cur_fan_w;
+
+        float   cv = (p->center_val >= 0.0f)
+                        ? p->center_val
+                        : 0.0f;
+
+        int fan_top_y = y - length / 2 + h / 2;
+        int fan_val_y = length * value;
+        int fan_cv_y =  length * cv;
+
+        if (p->direction)
+        {
+            sign_cur_fan_w = 1 * p->cur_fan.width;
+            offset = w;
+        }
+        else
+        { 
+            sign_cur_fan_w = -1 * p->cur_fan.width;
+            offset = 0;
+        }
+
+        /* fan background */
+        cairo_move_to(      cr, x + offset,     y);
+        /* bottom from slider */
+        cairo_line_to (     cr, x + offset + sign_cur_fan_w,
+                                fan_top_y);
+        /* vertical fan line */
+        cairo_rel_line_to ( cr, 0,              length);
+        /* top line back to slider */
+        cairo_line_to(      cr, x + offset,     y + h);
+
+        if (supports_alpha)
+            cairo_set_source_rgba( cr, r1, g1, b1, a1);
+        else
+            cairo_set_source_rgb( cr, r1, g1, b1);
+
+        cairo_fill( cr);
+
+        if (bitmap)
+        {
+            /*  a bitmap is used as a mask so that when the fan slider is
+                drawn on a non-composited display it does not wipe out the
+                rest of the display for the duration of its showing */
+            return;
+        }
+
+        /*  fan value foreground...
+            starting at slider */
+        cairo_move_to ( cr, x + offset, y + h - cv * h);
+        /* center line from slider */
+        cairo_line_to(  cr, x + offset + sign_cur_fan_w,
+                            fan_top_y + length - fan_cv_y);
+        /* vertical line from center value to value */
+        cairo_rel_line_to(  cr, 0,  (fan_cv_y - fan_val_y));
+        /* value line back to slider */
+        cairo_line_to(  cr, x + offset, y + h - h * value);
+
+        if (supports_alpha)
+            cairo_set_source_rgba( cr, r2, g2, b2, a2);
+        else
+            cairo_set_source_rgb( cr, r2, g2, b2);
+
+        cairo_fill(cr);
+    }
+    else /* (p->orientation == GTK_ORIENTATION_HORIZONTAL) */
+    {
+        int     sign_cur_fan_h;
+        float   cv = (p->center_val >= 0.0f)
+                        ? p->center_val
+                        : 0.0f;
+
+        int fan_left_x =    x - length / 2 + w / 2;
+        int fan_val_x =     length * value;
+        int fan_cv_x =      length * cv;
+
+        if (p->direction)
+        {
+            sign_cur_fan_h = 1 * p->cur_fan.height;
+            offset = h;
+        }
+        else
+        {
+            sign_cur_fan_h = -1 * p->cur_fan.height;
+            offset = 0;
+        }
+
+        /* fan background */
+        cairo_move_to (     cr, x,          y + offset);
+        /* left-hand line from slider */
+        cairo_line_to ( cr, fan_left_x,     y + offset + sign_cur_fan_h);
+        /* horizontal fan line */
+        cairo_rel_line_to ( cr, length,     0);
+        /* right-hand line back to slider */
+        cairo_line_to(      cr, x + w,      y + offset);
+
+        if (supports_alpha)
+            cairo_set_source_rgba( cr, r1, g1, b1, a1);
+        else
+            cairo_set_source_rgb( cr, r1, g1, b1);
+
+        cairo_fill(cr);
+
+        if (bitmap)
+        {
+            /*  a bitmap is used as a mask so that when the fan slider is
+                drawn on a non-composited display it does not wipe out the
+                rest of the display for the duration of its showing */
+            return;
+        }
+
+        /*  fan value foreground...
+            starting at slider */
+        cairo_move_to ( cr, x + cv * w,     y + offset);
+        /* left-hand line from slider */
+        cairo_line_to ( cr, fan_left_x + fan_cv_x,
+                            y + offset + sign_cur_fan_h);
+        /* horizontal line from center value to value */
+        cairo_rel_line_to ( cr, fan_val_x - fan_cv_x,   0);
+        /* value line back to slider */
+        cairo_line_to (cr,  x + w * value,  y + offset);
+
+        if (supports_alpha)
+            cairo_set_source_rgba( cr, r2, g2, b2, a2);
+        else
+            cairo_set_source_rgb( cr, r2, g2, b2);
+
+        cairo_fill(cr);
+
+    }
+}
+
+
+static void phin_fan_slider_draw_fan (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    GtkWidget* widget = GTK_WIDGET (slider);
+
+    int     x, y, w, h;
+    int     length;
+    double  value;
+    cairo_t* cr;
+
+    if (!gtk_widget_is_drawable (p->fan_window))
+        return;
+
+    phin_fan_slider_calc_layout(slider, &x, &y, &w, &h);
+
+    {
+        int root_x, root_y;
+        gdk_window_get_origin(gtk_widget_get_window(widget),
+                                                    &root_x, &root_y);
+        x += root_x;
+        y += root_y;
+    }
+
+    length = phin_fan_slider_get_fan_length (slider);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        if (p->inverted)
+            value = 1.0 - gtk_adjustment_get_value(p->adjustment_prv);
+        else
+            value = gtk_adjustment_get_value(p->adjustment_prv);
+    }
+    else
+    {
+        if (p->inverted)
+            value = 1.0 - gtk_adjustment_get_value(p->adjustment_prv);
+        else
+            value = gtk_adjustment_get_value(p->adjustment_prv);
+    }
+
+    cr = gdk_cairo_create(gtk_widget_get_window(p->fan_window));
+
+    fan_slider_draw(cr, NULL, slider, x, y, w, h, length, value);
+
+    cairo_destroy(cr);
+}
+
+
+static void phin_fan_slider_calc_layout (PhinFanSlider* slider,
+                                         int* x, int* y, int* w, int* h)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    GtkWidget* widget = GTK_WIDGET (slider);
+    int focus_width, focus_pad;
+    int pad;
+    GtkAllocation widget_alloc;
+
+    gtk_widget_style_get (widget,
+                          "focus-line-width", &focus_width,
+                          "focus-padding", &focus_pad,
+                          NULL);
+
+    pad = focus_width + focus_pad;
+
+    gtk_widget_get_allocation(widget, &widget_alloc);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        *x = widget_alloc.x + (widget_alloc.width - SLIDER_WIDTH) / 2;
+        *y = widget_alloc.y + pad;
+        *w = SLIDER_WIDTH;
+        *h = widget_alloc.height - 2 * pad;
+    }
+    else
+    {
+        *x = widget_alloc.x + pad;
+        *y = widget_alloc.y + (widget_alloc.height - SLIDER_WIDTH) / 2;
+        *w = widget_alloc.width - 2 * pad;
+        *h = SLIDER_WIDTH;
+    }
+}
+
+
+static void phin_fan_slider_update_value (PhinFanSlider* slider,
+                                          int x_root, int y_root)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    int length;
+    double oldval;
+    double value;
+    double inc;
+    GtkAllocation slider_alloc;
+
+    if (p->state != STATE_CLICKED)
+        return;
+
+    gtk_widget_get_allocation(GTK_WIDGET(slider), &slider_alloc);
+
+    oldval = p->val;
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        if (gtk_widget_is_drawable (p->fan_window)
+            && (x_root != p->xclick_root))
+        {
+            length = phin_fan_slider_get_fan_length (slider);
+            inc = ((p->yclick_root - y_root)
+                   * p->value_update_ratio / length);
+        }
+        else
+        {
+            inc = ((p->yclick_root - y_root)
+                   * p->value_update_ratio / slider_alloc.height);
+        }
+    }
+    else
+    {
+        if (gtk_widget_is_drawable (p->fan_window)
+            && (y_root != p->yclick_root))
+        {
+            length = phin_fan_slider_get_fan_length (slider);
+            inc = ((x_root - p->xclick_root)
+                   * p->value_update_ratio / length);
+        }
+        else
+        {
+            inc = ((x_root - p->xclick_root)
+                   * p->value_update_ratio / slider_alloc.width);
+        }
+    }
+
+    if (p->inverted)
+        inc = -inc;
+
+    p->val += inc;
+    p->val = CLAMP (p->val, 0, 1);
+
+    if (p->val != oldval)
+    {
+        value =
+            (gtk_adjustment_get_lower(p->adjustment_prv) * (1.0 - p->val)
+                 + gtk_adjustment_get_upper(p->adjustment_prv) * p->val);
+
+        g_signal_handlers_block_by_func(
+                                G_OBJECT(slider),
+                                phin_fan_slider_adjustment_value_changed,
+                                (gpointer) slider);
+
+        gtk_adjustment_set_value(p->adjustment_prv, value);
+
+        g_signal_emit(G_OBJECT(slider), signals[VALUE_CHANGED_SIGNAL], 0);
+
+        g_signal_handlers_unblock_by_func
+                                (G_OBJECT (slider),
+                                phin_fan_slider_adjustment_value_changed,
+                                (gpointer) slider);
+    }
+}
+
+
+static void phin_fan_slider_update_fan (PhinFanSlider* slider,
+                                        int x, int y)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    int width;
+    int height;
+    int w, h;
+    GtkAllocation fan_win_alloc;
+
+    if (p->state != STATE_CLICKED)
+        return;
+
+    gdk_window_get_geometry (p->event_window,
+                             NULL, NULL, &w, &h, NULL);
+
+    gtk_widget_get_allocation(p->fan_window, &fan_win_alloc);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        if (x > w)
+        {
+            width = x - w;
+            width = CLAMP (width, 0, p->fan_max_thickness);
+            p->cur_fan.width = width;
+            p->cur_fan.height = fan_max_height;
+            p->direction = 1;
+
+            if (!gtk_widget_get_visible (p->fan_window))
+                gtk_window_present (GTK_WINDOW (p->fan_window));
+
+            if (gtk_widget_get_visible (p->hint_window0))
+                gtk_widget_hide (p->hint_window0);
+
+            if (gtk_widget_get_visible (p->hint_window1))
+                gtk_widget_hide (p->hint_window1);
+        }
+        else if (x < 0)
+        {
+            width = -x;
+            width = CLAMP (width, 0, p->fan_max_thickness);
+            p->cur_fan.width = width;
+            p->cur_fan.height = fan_max_height;
+            p->direction = 0;
+
+            if (!gtk_widget_get_visible (p->fan_window))
+                gtk_window_present (GTK_WINDOW (p->fan_window));
+
+            if (gtk_widget_get_visible (p->hint_window0))
+                gtk_widget_hide (p->hint_window0);
+
+            if (gtk_widget_get_visible (p->hint_window1))
+                gtk_widget_hide (p->hint_window1);
+        }
+        else if (gtk_widget_get_visible (p->fan_window))
+        {
+            gtk_widget_hide (p->fan_window);
+        }
+    }
+    else
+    {
+        if (y > h)
+        {
+            height = y - h;
+            height = CLAMP (height, 0, p->fan_max_thickness);
+            p->cur_fan.width = fan_max_width;
+            p->cur_fan.height = height;
+            p->direction = 1;
+
+            if (!gtk_widget_get_visible (p->fan_window))
+                gtk_window_present (GTK_WINDOW (p->fan_window));
+
+            if (gtk_widget_get_visible (p->hint_window0))
+                gtk_widget_hide (p->hint_window0);
+
+            if (gtk_widget_get_visible (p->hint_window1))
+                gtk_widget_hide (p->hint_window1);
+        }
+        else if (y < 0)
+        {
+            height = -y;
+            height = CLAMP (height, 0, p->fan_max_thickness);
+            p->cur_fan.width = fan_max_width;
+            p->cur_fan.height = height;
+            p->direction = 0;
+
+            if (!gtk_widget_get_visible (p->fan_window))
+                gtk_window_present (GTK_WINDOW (p->fan_window));
+
+            if (gtk_widget_get_visible (p->hint_window0))
+                gtk_widget_hide (p->hint_window0);
+
+            if (gtk_widget_get_visible (p->hint_window1))
+                gtk_widget_hide (p->hint_window1);
+        }
+        else if (gtk_widget_get_visible (p->fan_window))
+        {
+            gtk_widget_hide (p->fan_window);
+        }
+    }
+}
+
+
+static int phin_fan_slider_get_fan_length (PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    GtkAllocation fan_win_alloc;
+    GtkAllocation slider_alloc;
+
+    gtk_widget_get_allocation(p->fan_window, &fan_win_alloc);
+    gtk_widget_get_allocation(GTK_WIDGET(slider), &slider_alloc);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        return 2 * (FAN_RISE / FAN_RUN)
+            * p->cur_fan.width
+            + slider_alloc.height;
+    }
+    else
+    {
+        return 2 * (FAN_RISE / FAN_RUN)
+            * p->cur_fan.height
+            + slider_alloc.width;
+    }
+}
+
+static gboolean phin_fan_slider_fan_expose (GtkWidget*      widget,
+                                            GdkEventExpose* event,
+                                            PhinFanSlider*  slider)
+{
+    (void)widget; (void)event;
+    phin_fan_slider_draw_fan (slider);
+
+    return TRUE;
+}
+
+static void phin_fan_slider_fan_show (GtkWidget* widget,
+                                      GtkWidget* slider)
+{
+    (void)widget; (void)slider;
+}
+
+static gboolean phin_fan_slider_hint_expose (GtkWidget* widget,
+                                             GdkEventExpose* event,
+                                             GtkWidget* slider)
+{
+    (void)widget; (void)slider; (void)event;
+    return TRUE;
+}
+
+
+/*  setup the hint arrows. these should hint to the user to move the
+ *  mouse in such a direction as to bring the fans out.
+ */
+
+static void phin_fan_slider_update_hints (PhinFanSlider* slider)
+{
+    (void)slider;
+/*
+    GdkRegion* oldclip0 = p->hint_clip0;
+    GdkRegion* oldclip1 = p->hint_clip1;
+
+    gtk_window_resize (GTK_WINDOW (p->hint_window0), 9, 9);
+    gtk_window_resize (GTK_WINDOW (p->hint_window1), 9, 9);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        GdkPoint points0[7] = {
+            { 8, 3 },
+            { 4, 3 },
+            { 4, 0 },
+            { 0, 4 },
+            { 4, 8 },
+            { 4, 6 },
+            { 8, 6 }
+        };
+
+        GdkPoint points1[7] = {
+            { 0, 3 },
+            { 4, 3 },
+            { 4, 0 },
+            { 8, 4 },
+            { 4, 8 },
+            { 4, 6 },
+            { 0, 6 }
+        };
+
+        p->hint_clip0 = gdk_region_polygon (points0, 7, GDK_EVEN_ODD_RULE);
+        p->hint_clip1 = gdk_region_polygon (points1, 7, GDK_EVEN_ODD_RULE);
+
+        gdk_window_shape_combine_region (p->hint_window0->window,
+                                         p->hint_clip0, 0, 0);
+        gdk_window_shape_combine_region (p->hint_window1->window,
+                                         p->hint_clip1, 0, 0);
+    }
+    else
+    {
+        GdkPoint points0[7] = {
+            { 3, 8 },
+            { 3, 4 },
+            { 0, 4 },
+            { 4,-1 },
+            { 9, 4 },
+            { 6, 4 },
+            { 6, 8 }
+        };
+
+        GdkPoint points1[7] = {
+            { 3, 0 },
+            { 3, 4 },
+            { 0, 4 },
+            { 4, 9 },
+            { 9, 4 },
+            { 6, 4 },
+            { 6, 0 }
+        };
+
+        p->hint_clip0 = gdk_region_polygon (points0, 7, GDK_EVEN_ODD_RULE);
+        p->hint_clip1 = gdk_region_polygon (points1, 7, GDK_EVEN_ODD_RULE);
+
+        gdk_window_shape_combine_region (p->hint_window0->window,
+                                         p->hint_clip0, 0, 0);
+        gdk_window_shape_combine_region (p->hint_window1->window,
+                                         p->hint_clip1, 0, 0);
+    }
+          
+    if (oldclip0 != NULL)
+        gdk_region_destroy (oldclip0);
+    if (oldclip1 != NULL)
+        gdk_region_destroy (oldclip1);
+*/
+}
+
+
+static void phin_fan_slider_adjustment_changed (GtkAdjustment* adj,
+                                                PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    GtkWidget* widget;
+    double adj_lower, adj_upper, adj_value;
+
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (slider));
+
+    widget = GTK_WIDGET (slider);
+
+    adj_lower = gtk_adjustment_get_lower(adj);
+    adj_upper = gtk_adjustment_get_upper(adj);
+    adj_value = gtk_adjustment_get_value(adj);
+
+    if (adj_lower < 0 && adj_upper > 0)
+    {
+        p->center_val = (-adj_lower / (adj_upper - adj_lower));
+    }
+    else
+    {
+        p->center_val = -1;
+    }
+
+    p->val = ((adj_value - adj_lower)
+                   / (adj_upper - adj_lower));
+
+    gtk_widget_queue_draw (GTK_WIDGET (slider));
+
+    if (gtk_widget_get_realized (widget))
+        gdk_window_process_updates (gtk_widget_get_window(widget), FALSE);
+
+    g_signal_emit (G_OBJECT (slider), signals[CHANGED_SIGNAL], 0);
+}
+
+static void phin_fan_slider_adjustment_value_changed (GtkAdjustment* adj,
+                                                      PhinFanSlider* slider)
+{
+    PhinFanSliderPrivate* p = PHIN_FAN_SLIDER_GET_PRIVATE (slider);
+    GtkWidget* widget;
+    double adj_lower, adj_upper, adj_value;
+
+    g_return_if_fail (PHIN_IS_FAN_SLIDER (slider));
+
+    widget = GTK_WIDGET (slider);
+    adj_lower = gtk_adjustment_get_lower(adj);
+    adj_upper = gtk_adjustment_get_upper(adj);
+    adj_value = gtk_adjustment_get_value(adj);
+
+    p->val = ((adj_value - adj_lower) / (adj_upper - adj_lower));
+
+    gtk_widget_queue_draw (widget);
+
+    if (gtk_widget_get_realized (widget))
+        gdk_window_process_updates (gtk_widget_get_window(widget), FALSE);
+
+    g_signal_emit (G_OBJECT (slider), signals[VALUE_CHANGED_SIGNAL], 0);
+
+    if (p->adjustment != NULL)
+    {
+        phin_fan_slider_get_value(slider);
+        /* update value of external adjustment */
+    }
+}
diff --git a/libphin/phinfanslider.h b/libphin/phinfanslider.h
new file mode 100644
index 0000000..93c2c0a
--- /dev/null
+++ b/libphin/phinfanslider.h
@@ -0,0 +1,118 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+/*  Phin is a fork of PHAT
+
+    Original Specimen author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __PHIN_FAN_SLIDER_H__
+#define __PHIN_FAN_SLIDER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PHIN_TYPE_FAN_SLIDER            (phin_fan_slider_get_type())
+#define PHIN_FAN_SLIDER(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj),  PHIN_TYPE_FAN_SLIDER, \
+                                        PhinFanSlider))
+
+#define PHIN_FAN_SLIDER_CLASS(klass)    \
+    (G_TYPE_CHECK_CLASS_CAST((klass),   PHIN_TYPE_FAN_SLIDER, \
+                                        PhinFanSliderClass))
+
+#define PHIN_IS_FAN_SLIDER(obj) \
+    (G_TYPE_CHECK_INSTANCE_TYPE((obj),  PHIN_TYPE_FAN_SLIDER))
+
+#define PHIN_IS_FAN_SLIDER_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE((klass),   PHIN_TYPE_FAN_SLIDER))
+
+
+typedef struct _PhinFanSliderClass PhinFanSliderClass;
+typedef struct _PhinFanSlider      PhinFanSlider;
+
+
+struct _PhinFanSlider
+{
+    GtkWidget parent;
+};
+
+
+struct _PhinFanSliderClass
+{
+    GtkWidgetClass parent_class;
+    /* private */
+    void (*value_changed) (PhinFanSlider* slider);
+    void (*changed)       (PhinFanSlider* slider);
+};
+
+/* whether to use fans on the sliders or not (requires compositing).*/
+void        phin_fan_slider_set_fans_active(gboolean enable_fans);
+gboolean    phin_fan_slider_get_fans_active(void);
+
+
+GType       phin_fan_slider_get_type(   void );
+void        phin_fan_slider_set_value(  PhinFanSlider*, double );
+void        phin_fan_slider_set_log(    PhinFanSlider*, gboolean );
+gboolean    phin_fan_slider_is_log(     PhinFanSlider* );
+double      phin_fan_slider_get_value(  PhinFanSlider* );
+
+void        phin_fan_slider_set_range(  PhinFanSlider*, double lower,
+                                                        double upper );
+void        phin_fan_slider_get_range(  PhinFanSlider*, double* lower,
+                                                        double* upper );
+
+void            phin_fan_slider_set_adjustment( PhinFanSlider*,
+                                                GtkAdjustment* );
+GtkAdjustment*  phin_fan_slider_get_adjustment( PhinFanSlider* );
+
+void        phin_fan_slider_set_inverted(   PhinFanSlider*, gboolean); 
+gboolean    phin_fan_slider_get_inverted(   PhinFanSlider*);
+
+void        phin_fan_slider_set_default_value(  PhinFanSlider*, gdouble ); 
+
+/* run once */
+void        phin_fan_slider_set_orientation(    PhinFanSlider*,
+                                                GtkOrientation );
+
+G_END_DECLS
+
+#endif /* __PHIN_FAN_SLIDER_H__ */
+
diff --git a/libphin/phinhfanslider.c b/libphin/phinhfanslider.c
new file mode 100644
index 0000000..671e4a7
--- /dev/null
+++ b/libphin/phinhfanslider.c
@@ -0,0 +1,79 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <gtk/gtk.h>
+
+
+#include "phinprivate.h"
+#include "phinfanslider.h"
+#include "phinhfanslider.h"
+
+
+G_DEFINE_TYPE(PhinHFanSlider, phin_hfan_slider, PHIN_TYPE_FAN_SLIDER);
+
+
+GtkWidget* phin_hfan_slider_new (GtkAdjustment* adjustment)
+{
+    PhinHFanSlider* slider;
+
+    gdouble adj_lower = gtk_adjustment_get_lower(adjustment);
+    gdouble adj_upper = gtk_adjustment_get_upper(adjustment);
+    gdouble adj_value = gtk_adjustment_get_value(adjustment);
+
+    g_assert (adj_lower < adj_upper);
+    g_assert((adj_value >= adj_lower)
+          && (adj_value <= adj_upper));
+
+    slider = g_object_new (PHIN_TYPE_HFAN_SLIDER, NULL);
+
+    phin_fan_slider_set_orientation(PHIN_FAN_SLIDER(slider),
+                                    GTK_ORIENTATION_HORIZONTAL);
+
+    phin_fan_slider_set_adjustment( PHIN_FAN_SLIDER (slider), adjustment);
+
+    return (GtkWidget*) slider;
+}
+
+
+GtkWidget* phin_hfan_slider_new_with_range (double value, double lower,
+                                            double upper, double step)
+{
+    GtkAdjustment* adj;
+
+    adj = (GtkAdjustment*)gtk_adjustment_new(value, lower, upper,
+                                                    step, step, 0);
+    return phin_hfan_slider_new (adj);
+}
+
+
+static void phin_hfan_slider_class_init(PhinHFanSliderClass* klass)
+{
+    phin_hfan_slider_parent_class = g_type_class_peek_parent(klass);
+}
+
+
+static void phin_hfan_slider_init(PhinHFanSlider* slider)
+{
+    (void)slider;
+    return;
+}
diff --git a/libphin/phinhfanslider.h b/libphin/phinhfanslider.h
new file mode 100644
index 0000000..5d370e4
--- /dev/null
+++ b/libphin/phinhfanslider.h
@@ -0,0 +1,76 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_HFAN_SLIDER_H__
+#define __PHIN_HFAN_SLIDER_H__
+
+#include <gtk/gtk.h>
+
+#include "phinfanslider.h"
+
+
+G_BEGIN_DECLS
+
+#define PHIN_TYPE_HFAN_SLIDER \
+    (phin_hfan_slider_get_type())
+
+#define PHIN_HFAN_SLIDER(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST((obj),  PHIN_TYPE_HFAN_SLIDER, \
+                                        PhinHFanSlider))
+
+#define PHIN_IS_HFAN_SLIDER(obj) \
+    (G_TYPE_CHECK_INSTANCE_TYPE((obj),  PHIN_TYPE_HFAN_SLIDER))
+
+
+#define PHIN_HFAN_SLIDER_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST ((klass),  PHIN_TYPE_HFAN_SLIDER, \
+                                        PhinHFanSliderClass))
+
+#define PHIN_IS_HFAN_SLIDER_CLASS(klass) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((klass), PHIN_TYPE_HFAN_SLIDER))
+
+
+typedef struct _PhinHFanSliderClass PhinHFanSliderClass;
+typedef struct _PhinHFanSlider      PhinHFanSlider;
+
+struct _PhinHFanSlider
+{
+    PhinFanSlider parent;
+};
+
+struct _PhinHFanSliderClass
+{
+    PhinFanSliderClass parent_class;
+};
+
+GType phin_hfan_slider_get_type ( );
+
+GtkWidget* phin_hfan_slider_new (GtkAdjustment* adjustment);
+
+GtkWidget* phin_hfan_slider_new_with_range (double value,
+                                            double lower,
+                                            double upper,
+                                            double step);
+G_END_DECLS
+
+#endif /* __PHIN_HFAN_SLIDER_H__ */
diff --git a/libphin/phinhkeyboard.c b/libphin/phinhkeyboard.c
new file mode 100644
index 0000000..68422e0
--- /dev/null
+++ b/libphin/phinhkeyboard.c
@@ -0,0 +1,67 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <gtk/gtk.h>
+
+#include "phinhkeyboard.h"
+
+
+G_DEFINE_TYPE(PhinHKeyboard, phin_hkeyboard, PHIN_TYPE_KEYBOARD);
+
+
+static void phin_hkeyboard_class_init(PhinHKeyboardClass* klass)
+{
+    phin_hkeyboard_parent_class = g_type_class_peek_parent(klass);
+}
+
+
+static void phin_hkeyboard_init(PhinHKeyboard* self)
+{
+    (void)self;
+}
+
+/**
+ * phin_hkeyboard_new:
+ * @adjustment: the #GtkAdjustment that the new keyboard will use for scrolling
+ * @numkeys: number of keys to create
+ * @show_labels: whether to label the C keys
+ *
+ * Creates a new #PhinHKeyboard.
+ *
+ * Returns: a newly created #PhinHKeyboard
+ * 
+ */
+GtkWidget* phin_hkeyboard_new(GtkAdjustment* adj,   int numkeys,
+                                                    gboolean show_labels)
+{
+    if (!adj)
+        adj = (GtkAdjustment*) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
+
+    return g_object_new(PHIN_TYPE_HKEYBOARD,
+                        "hadjustment",  adj,
+                        "shadow-type",  GTK_SHADOW_NONE,
+                        "orientation",  GTK_ORIENTATION_HORIZONTAL,
+                        "numkeys",      numkeys,
+                        "show-labels",  show_labels,
+                        NULL);
+}
diff --git a/libphin/phinhkeyboard.h b/libphin/phinhkeyboard.h
new file mode 100644
index 0000000..058af12
--- /dev/null
+++ b/libphin/phinhkeyboard.h
@@ -0,0 +1,76 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_HKEYBOARD__
+#define __PHIN_HKEYBOARD__
+
+#include <gtk/gtk.h>
+
+#include "phinkeyboard.h"
+
+
+G_BEGIN_DECLS
+
+
+#define PHIN_TYPE_HKEYBOARD         (phin_hkeyboard_get_type())
+
+#define PHIN_HKEYBOARD(obj)         (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                    PHIN_TYPE_HKEYBOARD, PhinHKeyboard))
+
+#define PHIN_HKEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                    PHIN_TYPE_HKEYBOARD, \
+                                    PhinHKeyboardClass))
+
+#define PHIN_IS_HKEYBOARD(obj)      (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                    PHIN_TYPE_HKEYBOARD))
+
+#define PHIN_IS_HKEYBOARD_CLASS(klass) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((klass), PHIN_TYPE_HKEYBOARD))
+
+
+typedef struct _PhinHKeyboardClass PhinHKeyboardClass;
+typedef struct _PhinHKeyboard PhinHKeyboard;
+
+
+struct _PhinHKeyboard
+{
+    /*< private >*/
+    PhinKeyboard parent;
+};
+
+
+struct _PhinHKeyboardClass
+{
+    /*< private >*/
+    PhinKeyboardClass parent_class;
+};
+
+
+GType       phin_hkeyboard_get_type(void);
+GtkWidget*  phin_hkeyboard_new(GtkAdjustment*,  int numkeys,
+                                                gboolean show_labels);
+
+
+G_END_DECLS
+
+#endif /* __PHIN_HKEYBOARD__ */
diff --git a/libphin/phinkeyboard.c b/libphin/phinkeyboard.c
new file mode 100644
index 0000000..ef53dd1
--- /dev/null
+++ b/libphin/phinkeyboard.c
@@ -0,0 +1,667 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <libgnomecanvas/libgnomecanvas.h>
+#include "phinkeyboard.h"
+
+
+/* properties */
+enum
+{
+    PROP_0,
+    PROP_ORIENTATION,
+    PROP_NUMKEYS,
+    PROP_SHOWLABELS,
+};
+
+/* signals */
+enum
+{
+    KEY_PRESSED,
+    KEY_RELEASED,
+    LAST_SIGNAL,
+};
+
+static int signals[LAST_SIGNAL];
+
+
+/* magic numbers */
+enum
+{
+    NUMKEYS = 128,
+    MINKEYS = 1,
+    MAXKEYS = 1000,
+    TEXT_POINTS = 7,
+};
+
+/* all colors are 32bits, RGBA, 8 bits (2 hex digits) per channel */
+
+/* natural (white) key colors */
+static const guint KEY_NAT_BG =     0xEEEEEEFF;
+static const guint KEY_NAT_HI =     0xFFFFFFFF;
+static const guint KEY_NAT_LOW =    0x000000FF;
+static const guint KEY_NAT_PRE =    0xFFFFFFFF;
+static const guint KEY_NAT_ON =     0xD7D7D7FF;
+static const guint KEY_NAT_SHAD =   0xAAAAAAFF;
+
+/* accidental (black) key colors */
+static const guint KEY_ACC_BG =     0x949494FF;
+static const guint KEY_ACC_HI =     0xC9C9C9FF;
+static const guint KEY_ACC_LOW =    0x000000FF;
+static const guint KEY_ACC_PRE =    0xA5A5A5FF;
+static const guint KEY_ACC_ON =     0x767676FF;
+static const guint KEY_ACC_SHAD =   0x4D4D4DFF;
+
+/* c text label color */
+static const guint KEY_TEXT_BG =    0x000000FF;
+
+
+typedef struct __Key _Key;
+
+struct __Key
+{
+    int index;
+    PhinKeyboard* keyboard;     /* the keyboard we belong to */
+    GnomeCanvasGroup* group;    /* the group this key belongs to */
+    GnomeCanvasItem* pre;       /* prelight rectangle */
+    GnomeCanvasItem* on;        /* active (depressed) rectangle */
+    GnomeCanvasItem* shad;      /* active shadow */
+};
+
+
+typedef struct _PhinKeyboardPrivate PhinKeyboardPrivate;
+
+#define PHIN_KEYBOARD_GET_PRIVATE(obj) \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), PHIN_TYPE_KEYBOARD, \
+                                        PhinKeyboardPrivate))
+
+struct _PhinKeyboardPrivate
+{
+    _Key    *keys;
+    int     nkeys;
+    int     label;
+
+    GnomeCanvas*    canvas;
+    GtkOrientation  orientation;
+};
+
+
+G_DEFINE_TYPE(PhinKeyboard, phin_keyboard, GTK_TYPE_VIEWPORT)
+
+
+static void phin_keyboard_class_init(PhinKeyboardClass* klass);
+static void phin_keyboard_init(PhinKeyboard* self);
+static void phin_keyboard_dispose(GObject* object);
+static void phin_keyboard_set_property(GObject          *object,
+                                       guint             prop_id,
+                                       const GValue     *value,
+                                       GParamSpec       *pspec);
+static void phin_keyboard_get_property(GObject          *object,
+                                       guint             prop_id,
+                                       GValue           *value,
+                                       GParamSpec       *pspec);
+
+
+static void phin_keyboard_class_init(PhinKeyboardClass* klass)
+{
+    GObjectClass* object_class = G_OBJECT_CLASS(klass);
+
+    phin_keyboard_parent_class = g_type_class_peek_parent(klass);
+
+    object_class->dispose = phin_keyboard_dispose;
+
+    g_type_class_add_private(object_class, sizeof(PhinKeyboardPrivate));
+
+    object_class->set_property = phin_keyboard_set_property;
+    object_class->get_property = phin_keyboard_get_property;
+
+    g_object_class_install_property(object_class,
+                                    PROP_ORIENTATION,
+                                    g_param_spec_enum("orientation",
+                                                      "Orientation",
+                    "How the keyboard should be arranged on the screen",
+                                    GTK_TYPE_ORIENTATION,
+                                    GTK_ORIENTATION_VERTICAL,
+                                    G_PARAM_READWRITE
+                                  | G_PARAM_CONSTRUCT_ONLY));
+
+    g_object_class_install_property(object_class,
+                                    PROP_NUMKEYS,
+                                    g_param_spec_int("numkeys",
+                                                     "Number of Keys",
+                    "How many keys this keyboard should have",
+                                    MINKEYS,
+                                    MAXKEYS,
+                                    NUMKEYS,
+                                    G_PARAM_READWRITE
+                                  | G_PARAM_CONSTRUCT_ONLY));
+
+    g_object_class_install_property(object_class,
+                                    PROP_SHOWLABELS,
+                                    g_param_spec_boolean("show-labels",
+                                                         "Show Labels",
+                    "Whether C keys should be labeled or not",
+                                    TRUE,
+                                    G_PARAM_READWRITE
+                                  | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * PhinKeyboard::key-pressed
+     * @keyboard: the object on which the signal was emitted
+     * @key: the index of the key that was pressed
+     *
+     * The "key-pressed" signal is emitted whenever a key is pressed.
+     *
+     */
+    signals[KEY_PRESSED] =
+        g_signal_new ("key-pressed",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (PhinKeyboardClass, key_pressed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__INT,
+                      G_TYPE_NONE, 1, G_TYPE_INT);
+
+    /**
+     * PhinKeyboard::key-released
+     * @keyboard: the object on which the signal was emitted
+     * @key: the index of the key that was pressed
+     *
+     * The "key-released" signal is emitted whenever a key is released.
+     *
+     */
+    signals[KEY_RELEASED] =
+        g_signal_new ("key-released",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (PhinKeyboardClass, key_released),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__INT,
+                      G_TYPE_NONE, 1, G_TYPE_INT);
+
+    klass->key_pressed = NULL;
+    klass->key_released = NULL;
+}
+
+
+static gboolean
+key_press_cb(GnomeCanvasItem* item, GdkEvent* event, _Key* key)
+{
+    (void)item;
+
+    switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+        gnome_canvas_item_show(key->on);
+        gnome_canvas_item_show(key->shad);
+        g_signal_emit(key->keyboard, signals[KEY_PRESSED], 0, key->index);
+        break;
+
+    case GDK_BUTTON_RELEASE:
+        gnome_canvas_item_hide(key->on);
+        gnome_canvas_item_hide(key->shad);
+        g_signal_emit(key->keyboard, signals[KEY_RELEASED], 0, key->index);
+        break;
+
+    case GDK_ENTER_NOTIFY:
+        gnome_canvas_item_show(key->pre);
+        break;
+
+    case GDK_LEAVE_NOTIFY:
+        gnome_canvas_item_hide(key->pre);
+        break;
+
+    default:
+        break;
+    }
+
+    return FALSE;
+}
+
+
+/* so much gayness in here, either I suck or gnome-canvas does; most
+ * likely, we both do */
+static void draw_key(PhinKeyboard* self, int index, int pos, guint bg,
+                     guint hi, guint low, guint pre, guint on, guint shad)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(self);
+
+    _Key* key = &p->keys[index];
+    GnomeCanvasPoints* points;
+    int x1;
+    int y1;
+    int x2;
+    int y2;
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        x1 = 0;
+        y1 = pos + 1;           /* teh gayz0r */
+        x2 = PHIN_KEYBOARD_KEY_LENGTH - 1;
+        y2 = pos - PHIN_KEYBOARD_KEY_WIDTH + 1;
+    }
+    else
+    {
+        x1 = pos + PHIN_KEYBOARD_KEY_WIDTH - 1;
+        y1 = 0;
+        x2 = pos;
+        y2 = PHIN_KEYBOARD_KEY_LENGTH - 1;
+    }
+
+    /* key group */
+    key->group = (GnomeCanvasGroup*)
+        gnome_canvas_item_new(  gnome_canvas_root(p->canvas),
+                                gnome_canvas_group_get_type(), NULL);
+
+    g_signal_connect(G_OBJECT(key->group), "event",
+                     G_CALLBACK(key_press_cb), (gpointer)key);
+
+    key->index = index;
+    key->keyboard = self;
+
+    /* draw main key rect */
+    gnome_canvas_item_new(key->group,
+                          gnome_canvas_rect_get_type(),
+                          "x1", (gdouble)x1,
+                          "y1", (gdouble)y1,
+                          "x2", (gdouble)x2,
+                          "y2", (gdouble)y2,
+                          "fill-color-rgba", bg,
+                          NULL);
+
+    /* draw prelight rect */
+    key->pre = gnome_canvas_item_new(key->group,
+                                     gnome_canvas_rect_get_type(),
+                                     "x1", (gdouble)x1,
+                                     "y1", (gdouble)y1,
+                                     "x2", (gdouble)x2,
+                                     "y2", (gdouble)y2,
+                                     "fill-color-rgba", pre,
+                                     NULL);
+
+    gnome_canvas_item_hide(key->pre);
+
+    /* draw key highlight */
+    points = gnome_canvas_points_new(3);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        points->coords[0] = x1+1;   points->coords[1] = y1;
+        points->coords[2] = x1+1;   points->coords[3] = y2+1;
+        points->coords[4] = x2;     points->coords[5] = y2+1;
+    }
+    else
+    {
+        points->coords[0] = x1;     points->coords[1] = y1+1;
+        points->coords[2] = x2;     points->coords[3] = y1+1;
+        points->coords[4] = x2;     points->coords[5] = y2;
+    }
+
+    gnome_canvas_item_new(key->group,
+                          gnome_canvas_line_get_type(),
+                          "points", points,
+                          "width-units", (gdouble)1,
+                          "fill-color-rgba", hi,
+                          NULL);
+
+    gnome_canvas_points_unref(points);
+                        
+    /* draw key border */
+    points = gnome_canvas_points_new(4);
+    
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        points->coords[0] = x1;     points->coords[1] = y1;
+        points->coords[2] = x1;     points->coords[3] = y2;
+        points->coords[4] = x2;     points->coords[5] = y2;
+        points->coords[6] = x2;     points->coords[7] = y1;
+    }
+    else
+    {
+        points->coords[0] = x2;     points->coords[1] = y1;
+        points->coords[2] = x1;     points->coords[3] = y1;
+        points->coords[4] = x1;     points->coords[5] = y2;
+        points->coords[6] = x2;     points->coords[7] = y2;
+    }
+
+    gnome_canvas_item_new(key->group,
+                          gnome_canvas_line_get_type(),
+                          "points", points,
+                          "width-units", (gdouble)1,
+                          "fill-color-rgba", low,
+                          NULL);
+
+    gnome_canvas_points_unref(points);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        /* draw active rect */
+        key->on = gnome_canvas_item_new(key->group,
+                                        gnome_canvas_rect_get_type(),
+                                        "x1", (gdouble)x1+1,
+                                        "y1", (gdouble)y1,
+                                        "x2", (gdouble)x2,
+                                        "y2", (gdouble)y2+1,
+                                        "fill-color-rgba", on,
+                                        NULL);
+    }
+    else
+    {
+        /* draw active rect */
+        key->on = gnome_canvas_item_new(key->group,
+                                        gnome_canvas_rect_get_type(),
+                                        "x1", (gdouble)x1,
+                                        "y1", (gdouble)y1+1,
+                                        "x2", (gdouble)x2,
+                                        "y2", (gdouble)y2,
+                                        "fill-color-rgba", on,
+                                        NULL);
+    }
+
+    gnome_canvas_item_hide(key->on);
+
+    /* draw active shadow */
+    points = gnome_canvas_points_new(6);
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        points->coords[0] = x1+1;   points->coords[1] = y1;
+        points->coords[2] = x1+1;   points->coords[3] = y2+1;
+        points->coords[4] = x2;     points->coords[5] = y2+1;
+        points->coords[6] = x2;     points->coords[7] = y2 + 3;
+        points->coords[8] = x1 + 3; points->coords[9] = y2 + 3;
+        points->coords[10] = x1 + 3;points->coords[11] = y1;
+    }
+    else
+    {
+        points->coords[0] = x1;     points->coords[1] = y1 + 1;
+        points->coords[2] = x2;     points->coords[3] = y1 + 1;
+        points->coords[4] = x2;     points->coords[5] = y2;
+        points->coords[6] = x2 + 2; points->coords[7] = y2;
+        points->coords[8] = x2 + 2; points->coords[9] = y1 + 3;
+        points->coords[10] = x1;    points->coords[11] = y1 + 3;
+    }
+
+    key->shad = gnome_canvas_item_new(key->group,
+                                      gnome_canvas_polygon_get_type(),
+                                      "points", points,
+                                      "fill-color-rgba", shad,
+                                      NULL);
+    gnome_canvas_item_hide(key->shad);
+    gnome_canvas_points_unref(points);
+
+    /* draw label if applicable */
+    if (p->label && (index % 12) == 0)
+    {
+        char* s = g_strdup_printf("%d", index / 12);
+
+        if (p->orientation == GTK_ORIENTATION_VERTICAL)
+        {
+            gnome_canvas_item_new(key->group,
+                        gnome_canvas_text_get_type(),
+                        "text", s,
+                        "x", (gdouble)(x2 - 2),
+                        "y", (gdouble)(y1 - (PHIN_KEYBOARD_KEY_WIDTH / 2)),
+                        "anchor", GTK_ANCHOR_EAST,
+                        "fill-color-rgba", (gint)KEY_TEXT_BG,
+                        "font", "sans",
+                        "size-points", (gdouble)TEXT_POINTS, NULL);
+        }
+        else
+        {
+            gnome_canvas_item_new(key->group,
+                        gnome_canvas_text_get_type(),
+                        "text", s,
+                        "x", (gdouble)(x1 - (PHIN_KEYBOARD_KEY_WIDTH / 2)),
+                        "y", (gdouble)(y2 - 2),
+                        "anchor", GTK_ANCHOR_SOUTH,
+                        "fill-color-rgba", (gint)KEY_TEXT_BG,
+                        "font", "sans",
+                        "size-points", (gdouble)TEXT_POINTS,
+                        "justification", GTK_JUSTIFY_CENTER, NULL);
+        }
+
+        g_free(s);
+    }
+}
+
+
+static void draw_keyboard(PhinKeyboard* self)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(self);
+    int i, pos, note;
+
+    /* make sure our construction properties are set (this is
+     * _lame_ass_) */
+    if (p->nkeys < 0 || p->label < 0)
+        return;
+
+    p->keys = g_new(_Key, p->nkeys);
+
+    /* orientation */
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        gtk_widget_set_size_request(GTK_WIDGET(self),
+                                    PHIN_KEYBOARD_KEY_LENGTH, 0);
+        gtk_widget_set_size_request(GTK_WIDGET(p->canvas),
+                                    PHIN_KEYBOARD_KEY_LENGTH,
+                                    (PHIN_KEYBOARD_KEY_WIDTH * p->nkeys));
+        gnome_canvas_set_scroll_region(p->canvas, 0, 0,
+                                    PHIN_KEYBOARD_KEY_LENGTH - 1,
+                                    (PHIN_KEYBOARD_KEY_WIDTH * p->nkeys)-1);
+    }
+    else
+    {
+        gtk_widget_set_size_request(GTK_WIDGET(self), 0,
+                                    PHIN_KEYBOARD_KEY_LENGTH);
+        gtk_widget_set_size_request(GTK_WIDGET(p->canvas),
+                                    (PHIN_KEYBOARD_KEY_WIDTH * p->nkeys),
+                                    PHIN_KEYBOARD_KEY_LENGTH);
+        gnome_canvas_set_scroll_region(p->canvas, 0, 0,
+                                    (PHIN_KEYBOARD_KEY_WIDTH * p->nkeys)-1,
+                                    PHIN_KEYBOARD_KEY_LENGTH - 1);
+    }
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+    {
+        pos = (PHIN_KEYBOARD_KEY_WIDTH * p->nkeys) - 1;
+
+        for (i = note = 0; i < p->nkeys; ++i, ++note)
+        {
+            if (note > 11)
+                note = 0;
+
+            switch (note)
+            {
+            case 0: case 2: case 4: case 5: case 7: case 9: case 11:
+                draw_key(self, i, pos, KEY_NAT_BG, KEY_NAT_HI,
+                         KEY_NAT_LOW, KEY_NAT_PRE,
+                         KEY_NAT_ON, KEY_NAT_SHAD);
+                break;
+
+            default:
+                draw_key(self, i, pos, KEY_ACC_BG, KEY_ACC_HI,
+                         KEY_ACC_LOW, KEY_ACC_PRE,
+                         KEY_ACC_ON, KEY_ACC_SHAD);
+                break;
+            }
+            pos -= PHIN_KEYBOARD_KEY_WIDTH;
+        }
+    }
+    else
+    {
+        pos = 0;
+
+        for (i = note = 0; i < p->nkeys; ++i, ++note)
+        {
+            if (note > 11)
+                note = 0;
+
+            switch (note)
+            {
+            case 0: case 2: case 4: case 5: case 7: case 9: case 11:
+                draw_key(self, i, pos, KEY_NAT_BG, KEY_NAT_HI,
+                         KEY_NAT_LOW, KEY_NAT_PRE,
+                         KEY_NAT_ON, KEY_NAT_SHAD);
+                break;
+
+            default:
+                draw_key(self, i, pos, KEY_ACC_BG, KEY_ACC_HI,
+                         KEY_ACC_LOW, KEY_ACC_PRE,
+                         KEY_ACC_ON, KEY_ACC_SHAD);
+                break;
+            }
+
+            pos += PHIN_KEYBOARD_KEY_WIDTH;
+        }
+    }
+}
+
+
+
+static void phin_keyboard_init(PhinKeyboard* self)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(self);
+    p->keys = NULL;
+    p->nkeys = -1;
+    p->orientation = -1;
+    p->label = -1;
+
+    p->canvas = (GnomeCanvas*) gnome_canvas_new();
+    gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(p->canvas));
+    gtk_widget_show(GTK_WIDGET(p->canvas));
+}
+ 
+
+static void phin_keyboard_set_property(GObject          *object,
+                                       guint             prop_id,
+                                       const GValue     *value,
+                                       GParamSpec       *pspec)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(object);
+
+    switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+        p->orientation = g_value_get_enum(value);
+        break;
+
+    case PROP_NUMKEYS:
+        p->nkeys = g_value_get_int(value);
+        break;
+
+    case PROP_SHOWLABELS:
+        p->label = g_value_get_boolean(value);
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        return;
+    }
+
+    draw_keyboard(PHIN_KEYBOARD(object));
+}
+
+
+static void phin_keyboard_get_property(GObject          *object,
+                                       guint             prop_id,
+                                       GValue           *value,
+                                       GParamSpec       *pspec)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(object);
+
+    switch (prop_id)
+    {
+    case PROP_ORIENTATION:
+        g_value_set_enum (value, p->orientation);
+        break;
+
+    case PROP_NUMKEYS:
+        g_value_set_int(value, p->nkeys);
+        break;
+
+    case PROP_SHOWLABELS:
+        g_value_set_boolean(value, p->label);
+        break;
+
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+
+static void phin_keyboard_dispose(GObject* object)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(object);
+
+    g_free(p->keys);
+    p->keys = NULL;
+}
+
+
+/**
+ * phin_keyboard_get_adjustment:
+ * @keyboard: a #PhinKeyboard
+ *
+ * Retrives the current adjustment in use by @keyboard.
+ *
+ * Returns: @keyboard's current #GtkAdjustment
+ *
+ */
+GtkAdjustment* phin_keyboard_get_adjustment(PhinKeyboard* kb)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(kb);
+    GtkAdjustment* adj;
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+        g_object_get(kb, "vadjustment", &adj, (char *)NULL);
+    else
+        g_object_get(kb, "hadjustment", &adj, (char *)NULL);
+
+    return adj;
+}
+
+
+/**
+ * phin_keyboard_set_adjustment:
+ * @keyboard: a #PhinKeyboard
+ * @adjustment: a #GtkAdjustment
+ *
+ * Sets the adjustment used by @keyboard.
+ *
+ */
+void phin_keyboard_set_adjustment(PhinKeyboard* kb, GtkAdjustment* adj)
+{
+    PhinKeyboardPrivate* p = PHIN_KEYBOARD_GET_PRIVATE(kb);
+
+    if (!adj)
+        return;
+
+    if (p->orientation == GTK_ORIENTATION_VERTICAL)
+        g_object_set(kb, "vadjustment", adj, (char *)NULL);
+    else
+        g_object_set(kb, "hadjustment", adj, (char *)NULL);
+}
diff --git a/libphin/phinkeyboard.h b/libphin/phinkeyboard.h
new file mode 100644
index 0000000..aa1704b
--- /dev/null
+++ b/libphin/phinkeyboard.h
@@ -0,0 +1,80 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_KEYBOARD__
+#define __PHIN_KEYBOARD__
+
+#include <gtk/gtk.h>
+#include <libgnomecanvas/libgnomecanvas.h>
+
+G_BEGIN_DECLS
+
+#define PHIN_TYPE_KEYBOARD          (phin_keyboard_get_type())
+
+#define PHIN_KEYBOARD(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                    PHIN_TYPE_KEYBOARD, PhinKeyboard))
+
+#define PHIN_KEYBOARD_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                    PHIN_TYPE_KEYBOARD, PhinKeyboardClass))
+
+#define PHIN_IS_KEYBOARD(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                    PHIN_TYPE_KEYBOARD))
+
+#define PHIN_IS_KEYBOARD_CLASS(klass) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((klass), PHIN_TYPE_KEYBOARD))
+
+
+typedef struct _PhinKeyboard        PhinKeyboard;
+typedef struct _PhinKeyboardClass   PhinKeyboardClass;
+
+
+/* key dimensions */
+enum
+{
+    PHIN_KEYBOARD_KEY_WIDTH  = 13,
+    PHIN_KEYBOARD_KEY_LENGTH = 33,
+};
+
+
+struct _PhinKeyboard
+{
+    GtkViewport parent;
+};
+
+
+struct _PhinKeyboardClass
+{
+    GtkViewportClass parent_class;
+    /*< private >*/
+    void (*key_pressed)(PhinKeyboard* keyboard, int key);
+    void (*key_released)(PhinKeyboard* keyboard, int key);
+};
+
+
+GType           phin_keyboard_get_type      (void);
+GtkAdjustment*  phin_keyboard_get_adjustment(PhinKeyboard*);
+void            phin_keyboard_set_adjustment(PhinKeyboard*, GtkAdjustment*); 
+
+G_END_DECLS
+
+#endif /* __PHIN_KEYBOARD__ */
diff --git a/libphin/phinprivate.c b/libphin/phinprivate.c
new file mode 100644
index 0000000..9e9e4fd
--- /dev/null
+++ b/libphin/phinprivate.c
@@ -0,0 +1,93 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include "phinprivate.h"
+
+void phin_warp_pointer (int xsrc, int ysrc,
+                        int xdest, int ydest)
+{
+    (void)xsrc; (void)ysrc;
+/* this function used to use the Xlib function:
+    XWarpPointer (GDK_DISPLAY ( ), None, None, 0, 0, 0, 0, x, y);
+
+    where:
+    x = xdest - xsrc;
+    y = ydest - ysrc;
+*/
+    gdk_display_warp_pointer (gdk_display_get_default(),
+                              gdk_screen_get_default(), xdest, ydest);
+}
+
+
+
+void set_cairo_rgba_from_gdk(cairo_t* cr, GdkColor* col, double a)
+{
+    double r = col->red / 65535.0f;
+    double g = col->green / 65535.0f;
+    double b = col->blue / 65535.0f;
+
+    if (supports_alpha)
+        cairo_set_source_rgba(cr, r, g, b, a);
+    else
+        cairo_set_source_rgb(cr, r, g, b);
+}
+
+
+void gdk_col_to_double(GdkColor* c, double* r, double* g, double* b)
+{
+    *r = c->red / 65535.0f;
+    *g = c->green / 65535.0f;
+    *b = c->blue / 65535.0f;
+}
+
+
+/* Only some X servers support alpha channels. Always have a fallback */
+gboolean supports_alpha = FALSE;
+
+
+/* Only some X servers support alpha channels. Always have a fallback */
+void phin_screen_changed(GtkWidget *widget, GdkScreen *old_screen,
+                                                    gpointer userdata)
+{
+    (void)old_screen; (void)userdata;
+    /* To check if the display supports alpha channels, get the colormap */
+    GdkScreen *screen = gtk_widget_get_screen(widget);
+    GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
+
+    if (!colormap)
+    {
+        debug("Your screen does not support alpha channels!\n");
+        colormap = gdk_screen_get_rgb_colormap(screen);
+        supports_alpha = FALSE;
+    }
+    else
+    {
+        debug("Your screen supports alpha channels!\n");
+        supports_alpha = TRUE;
+    }
+
+    /* Now we have a colormap appropriate for the screen, use it */
+    gtk_widget_set_colormap(widget, colormap);
+}
diff --git a/libphin/phinprivate.h b/libphin/phinprivate.h
new file mode 100644
index 0000000..bcd75aa
--- /dev/null
+++ b/libphin/phinprivate.h
@@ -0,0 +1,61 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_PRIVATE_H__
+#define __PHIN_PRIVATE_H__
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include "config.h"
+
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+
+/*  no mechanism to set debug for petri-foo but not phin, so force no debug
+    here:
+ */
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG 0
+#endif
+
+
+#define debug(...) if (DEBUG) fprintf (stderr, __VA_ARGS__)
+
+
+extern gboolean supports_alpha;
+
+void phin_screen_changed(GtkWidget *widget, GdkScreen *old_screen,
+                                                    gpointer userdata);
+
+void phin_warp_pointer (int xsrc, int ysrc, int xdest, int ydest);
+
+void set_cairo_rgba_from_gdk(cairo_t* cr, GdkColor* col, double a);
+void gdk_col_to_double(GdkColor* c, double* r, double* g, double* b);
+
+
+#endif /* __PHIN_PRIVATE_H__ */
diff --git a/libphin/phinsliderbutton.c b/libphin/phinsliderbutton.c
new file mode 100644
index 0000000..3976e6b
--- /dev/null
+++ b/libphin/phinsliderbutton.c
@@ -0,0 +1,1770 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "phinprivate.h"
+#include "phinsliderbutton.h"
+
+/* hilite states */
+enum
+{
+    LEFT_ARROW = 1,
+    RIGHT_ARROW,
+    LABEL,
+};
+
+/* action states */
+enum
+{
+    STATE_NORMAL,
+    STATE_PRESSED,
+    STATE_SLIDE,
+    STATE_ENTRY,
+    STATE_SCROLL,
+};
+
+/* magic numbers */
+enum
+{
+    SCROLL_THRESHOLD = 4,
+    DIGITS = 2,
+    MAXDIGITS = 20,
+};
+
+/* signals */
+enum
+{
+    CHANGED_SIGNAL,
+    VALUE_CHANGED_SIGNAL,
+    LAST_SIGNAL,
+};
+
+static int signals[LAST_SIGNAL];
+
+
+typedef struct _PhinSliderButtonPrivate PhinSliderButtonPrivate;
+
+#define PHIN_SLIDER_BUTTON_GET_PRIVATE(obj) \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+    PHIN_TYPE_SLIDER_BUTTON, PhinSliderButtonPrivate))
+
+
+struct _PhinSliderButtonPrivate
+{
+    GtkAdjustment*  adjustment;
+
+    GdkCursor*  arrow_cursor;
+    GdkCursor*  empty_cursor;
+    GdkWindow*  event_window;
+    GtkWidget*  left_arrow;
+    GtkWidget*  right_arrow;
+    GtkWidget*  label;
+    GtkWidget*  prefix_label;
+    GtkWidget*  postfix_label;
+    GtkWidget*  entry;
+
+    char*   prefix;
+    char*   postfix;
+    int     digits;
+    int     hilite;
+    int     state;
+    int     xpress_root;
+    int     ypress_root;
+    int     xpress;
+    int     ypress;
+    int     firstrun;
+    guint   threshold;
+    gboolean slid;
+};
+
+/* forward declarations */
+G_DEFINE_TYPE(PhinSliderButton, phin_slider_button, GTK_TYPE_HBOX)
+
+static void phin_slider_button_dispose(         GObject* object);
+static void phin_slider_button_realize(         GtkWidget* widget);
+static void phin_slider_button_unrealize(       GtkWidget* widget);
+static void phin_slider_button_map(             GtkWidget* widget);
+static void phin_slider_button_unmap(           GtkWidget* widget);
+static void phin_slider_button_size_allocate(   GtkWidget* widget,
+                                                GtkAllocation*);
+
+static gboolean phin_slider_button_expose(          GtkWidget*,
+                                                    GdkEventExpose*);
+
+static void phin_slider_button_adjustment_changed(  GtkAdjustment*,
+                                                    PhinSliderButton* );
+
+static void phin_slider_button_adjustment_value_changed(GtkAdjustment*,
+                                                        PhinSliderButton*);
+
+static void phin_slider_button_entry_activate(      GtkEntry*,
+                                                    PhinSliderButton*);
+
+static gboolean phin_slider_button_entry_focus_out( GtkEntry*,
+                                                    GdkEventFocus*,
+                                                    PhinSliderButton*);
+
+static gboolean phin_slider_button_entry_key_press( GtkEntry* entry,
+                                                    GdkEventKey* event,
+                                                    PhinSliderButton*);
+
+static gboolean phin_slider_button_button_press(    GtkWidget*,
+                                                    GdkEventButton*);
+
+static gboolean phin_slider_button_button_release(  GtkWidget*,
+                                                    GdkEventButton*);
+
+static gboolean phin_slider_button_key_press(       GtkWidget*,
+                                                    GdkEventKey*);
+
+static gboolean phin_slider_button_scroll(          GtkWidget*,
+                                                    GdkEventScroll*);
+
+static gboolean phin_slider_button_enter_notify(    GtkWidget*,
+                                                    GdkEventCrossing*);
+
+static gboolean phin_slider_button_leave_notify(    GtkWidget*,
+                                                    GdkEventCrossing*);
+
+static gboolean phin_slider_button_motion_notify(   GtkWidget*,
+                                                    GdkEventMotion*);
+
+/* internal utility functions */
+static int      check_pointer(  PhinSliderButton*, int x, int y);
+static void     update_cursor(  PhinSliderButton*);
+static void     update_label(   PhinSliderButton*);
+static void     entry_cancel(   PhinSliderButton*);
+static void     update_size(    PhinSliderButton*);
+static char*    value_to_string(PhinSliderButton*, double value);
+
+
+
+/**
+ * phin_slider_button_new:
+ * @adjustment: the #GtkAdjustment that the new button will use
+ * @digits: number of decimal digits to display
+ * 
+ * Creates a new #PhinSliderButton.
+ *
+ * Returns: a newly created #PhinSliderButton
+ * 
+ */
+GtkWidget* phin_slider_button_new(GtkAdjustment* adj, int digits)
+{
+    PhinSliderButton* button;
+    PhinSliderButtonPrivate* p;
+
+    debug ("new\n");
+
+    gdouble adj_lower = gtk_adjustment_get_lower(adj);
+    gdouble adj_upper = gtk_adjustment_get_upper(adj);
+    gdouble adj_value = gtk_adjustment_get_value(adj);
+
+    g_assert (adj_lower < adj_upper);
+    g_assert((adj_value >= adj_lower)
+          && (adj_value <= adj_upper));
+
+    button = g_object_new (PHIN_TYPE_SLIDER_BUTTON, NULL);
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    p->digits = (digits >= 0) ? digits : DIGITS;
+
+    if (p->digits > MAXDIGITS)
+        p->digits = MAXDIGITS;
+
+    phin_slider_button_set_adjustment(button, adj);
+     
+    return (GtkWidget*) button;
+}
+
+
+/**
+ * phin_slider_button_new_with_range:
+ * @value: the initial value the new button should have
+ * @lower: the lowest value the new button will allow
+ * @upper: the highest value the new button will allow
+ * @step: increment added or subtracted when sliding
+ * @digits: number of decimal digits to display
+ *
+ * Creates a new #PhinSliderButton.  The slider will create a new
+ * #GtkAdjustment from @value, @lower, @upper, and @step.  If these
+ * parameters represent a bogus configuration, the program will
+ * terminate.
+ *
+ * Returns: a newly created #PhinSliderButton
+ *
+ */
+GtkWidget* phin_slider_button_new_with_range (double value, double lower,
+                                              double upper, double step,
+                                              int    digits)
+{
+    GtkAdjustment* adj;
+
+    adj = (GtkAdjustment*)
+        gtk_adjustment_new (value, lower, upper, step, step, 0);
+
+    return phin_slider_button_new (adj, digits);
+}
+
+
+
+/**
+ * phin_slider_button_set_adjustment:
+ * @button: a #PhinSliderButton
+ * @adjustment: a #GtkAdjustment
+ *
+ * Sets the adjustment used by @button.  If @adjustment is %NULL, a
+ * new adjustment with a value of zero and a range of [-1.0, 1.0] will
+ * be created.
+ *
+ */
+void phin_slider_button_set_adjustment (PhinSliderButton* button,
+                                        GtkAdjustment* adj)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    if (!adj)
+        adj = (GtkAdjustment*)
+                gtk_adjustment_new (0.0, -1.0, 1.0, 1.0, 1.0, 0.0);
+
+    if (p->adjustment)
+    {
+        g_signal_handlers_disconnect_by_func(p->adjustment,
+            phin_slider_button_adjustment_changed, (gpointer)button);
+        g_signal_handlers_disconnect_by_func(p->adjustment,
+            phin_slider_button_adjustment_value_changed, (gpointer)button);
+        g_object_unref(p->adjustment);
+    }
+
+    p->adjustment = adj;
+    g_object_ref (adj);
+    g_object_ref_sink (GTK_OBJECT(adj));
+
+    g_signal_connect (adj, "changed",
+                  G_CALLBACK(phin_slider_button_adjustment_changed),
+                  (gpointer) button);
+
+    g_signal_connect(adj, "value_changed",
+                  G_CALLBACK(phin_slider_button_adjustment_value_changed),
+                  (gpointer) button);
+
+    phin_slider_button_adjustment_changed (adj, button);
+    phin_slider_button_adjustment_value_changed (adj, button);
+}
+
+
+/**
+ * phin_slider_button_get_adjustment:
+ * @button: a #PhinSliderButton
+ *
+ * Retrives the current adjustment in use by @button.
+ *
+ * Returns: @button's current #GtkAdjustment
+ *
+ */
+GtkAdjustment* phin_slider_button_get_adjustment (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    if (!p->adjustment)
+        phin_slider_button_set_adjustment (button, NULL);
+
+    return p->adjustment;
+}
+
+
+
+/**
+ * phin_slider_button_set_value:
+ * @button: a #PhinSliderButton
+ * @value: a new value for the button
+ * 
+ * Sets the current value of the button.  If the value is outside the
+ * range of values allowed by @button, it will be clamped.  The button
+ * emits the "value-changed" signal if the value changes.
+ *
+ */
+void phin_slider_button_set_value (PhinSliderButton* button, double value)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    gdouble adj_lower = gtk_adjustment_get_lower(p->adjustment);
+    gdouble adj_upper = gtk_adjustment_get_upper(p->adjustment);
+
+    value = CLAMP (value, adj_lower, adj_upper);
+
+    gtk_adjustment_set_value (p->adjustment, value);
+}
+
+
+
+/**
+ * phin_slider_button_get_value:
+ * @button: a #PhinSliderButton
+ *
+ * Retrieves the current value of the button.
+ *
+ * Returns: current value of the button
+ *
+ */
+double phin_slider_button_get_value (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    return gtk_adjustment_get_value(p->adjustment);
+}
+
+
+
+/**
+ * phin_slider_button_set_range:
+ * @button: a #PhinSliderButton
+ * @lower: lowest allowable value
+ * @upper: highest allowable value
+ * 
+ * Sets the range of allowable values for the button, and clamps the
+ * button's current value to be between @lower and @upper.
+ */
+void phin_slider_button_set_range (PhinSliderButton* button,
+                                   double lower, double upper)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    double value;
+
+    g_return_if_fail (lower <= upper);
+
+    gtk_adjustment_set_lower(p->adjustment, lower);
+    gtk_adjustment_set_upper(p->adjustment, upper);
+
+    value = CLAMP (gtk_adjustment_get_value(p->adjustment),
+                   gtk_adjustment_get_lower(p->adjustment), /* ott? */
+                   gtk_adjustment_get_upper(p->adjustment));
+
+    gtk_adjustment_changed (p->adjustment);
+    gtk_adjustment_set_value (p->adjustment, value);
+}
+
+
+
+/**
+ * phin_slider_button_get_range:
+ * @button: a #PhinSliderButton
+ * @lower: retrieves lowest allowable value
+ * @upper: retrieves highest allowable value
+ *
+ * Places the range of allowable values for @button into @lower
+ * and @upper.  Either variable may be set to %NULL if you are not
+ * interested in its value.
+ *
+ */
+void phin_slider_button_get_range (PhinSliderButton* button,
+                                   double* lower, double* upper)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    if (lower)
+        *lower = gtk_adjustment_get_lower(p->adjustment);
+    if (upper)
+        *upper = gtk_adjustment_get_upper(p->adjustment);
+}
+
+
+
+/**
+ * phin_slider_button_set_increment:
+ * @button: a #PhinSliderButton
+ * @step: step increment value
+ * @page: page increment value
+ *
+ * Sets the increments the button should use.
+ *
+ */
+void phin_slider_button_set_increment (PhinSliderButton* button,
+                                       double step, double page)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    gtk_adjustment_set_step_increment(p->adjustment, step);
+    gtk_adjustment_set_page_increment(p->adjustment, page);
+}
+
+
+
+/**
+ * phin_slider_button_get_increment:
+ * @button: a #PhinSliderButton
+ * @step: retrieves step increment value
+ * @page: retrieves page increment value
+ *
+ * Places the button's increment values into @step and @page.  Either
+ * variable may be set to %NULL if you are not interested in its
+ * value.
+ *
+ */
+void phin_slider_button_get_increment (PhinSliderButton* button,
+                                       double* step, double* page)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    if (step)
+        *step = gtk_adjustment_get_step_increment(p->adjustment);
+    if (page)
+        *page = gtk_adjustment_get_page_increment(p->adjustment);
+}
+
+
+
+/**
+ * phin_slider_button_set_format:
+ * @button: a #PhinSliderButton
+ * @digits: number of decimal digits to display
+ * @prefix: text to prepend to number
+ * @postfix: text to append to number
+ *
+ * Sets the way @button renders it's label.  If the first character in
+ * either @prefix or @postfix is '\0' the corresponding parameter will
+ * be unset.  If you don't want to adjust @digits, set it to a
+ * negative value.  If you don't want to adjust @prefix and/or
+ * @postfix, set them to %NULL.
+ *
+ */
+void phin_slider_button_set_format (PhinSliderButton* button,
+                                    int         digits,
+                                    const char* prefix,
+                                    const char* postfix)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    if (digits >= 0)
+    {
+        p->digits = digits;
+
+        if (p->digits > MAXDIGITS)
+            p->digits = MAXDIGITS;
+    }
+
+    if (prefix)
+    {
+        g_free (p->prefix);
+
+        if (*prefix == '\0')
+        {
+            p->prefix = NULL;
+        }
+        else
+        {
+            p->prefix = g_strdup (prefix);
+        }
+    }
+
+    if (postfix)
+    {
+        g_free (p->postfix);
+
+        if (*postfix == '\0')
+        {
+            p->postfix = NULL;
+        }
+        else
+        {
+            p->postfix = g_strdup (postfix);
+        }
+    }
+
+    update_size (button);
+    update_label (button);
+}
+
+
+
+/**
+ * phin_slider_button_get_format:
+ * @button: a #PhinSliderButton
+ * @digits: retrieves the number of decimal digits to display
+ * @prefix: retrieves text prepended to number
+ * @postfix: retrieves text appended to number
+ *
+ * Retrieves the information @button uses to create its label.  The
+ * value returned will point to the button's local copy, so don't
+ * write to it.  Set the pointers for any value you aren't interested
+ * in to %NULL.
+ * 
+ */
+void phin_slider_button_get_format (PhinSliderButton* button,
+                                    int*   digits,
+                                    char** prefix,
+                                    char** postfix)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    if (digits)
+        *digits = p->digits;
+
+    if (prefix)
+        *prefix = p->prefix;
+
+    if (postfix)
+        *postfix = p->postfix;
+}
+
+
+/**
+ * phin_slider_button_set_threshold:
+ * @button: a #PhinSliderButton
+ * @threshold: an unsigned int >= 1
+ *
+ * Sets the threshold for @button.  The threshold is how far the user
+ * has to move the mouse to effect a change when sliding.
+ * 
+ */
+void phin_slider_button_set_threshold (PhinSliderButton* button,
+                                       guint threshold)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    g_return_if_fail (threshold != 0);
+
+    p->threshold = threshold;
+}
+
+
+/**
+ * phin_slider_button_get_threshold:
+ * @button: a #PhinSliderButton
+ *
+ * Retrieves the threshold for @button
+ *
+ * Returns: the threshold for @button, or -1 if @button is invalid
+ * 
+ */
+int phin_slider_button_get_threshold (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    return p->threshold;
+}
+
+
+static void phin_slider_button_class_init (PhinSliderButtonClass* klass)
+{
+    GObjectClass*   object_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+
+    phin_slider_button_parent_class = g_type_class_peek_parent(klass);
+
+    g_type_class_add_private(object_class, sizeof(PhinSliderButtonPrivate));
+
+    object_class->dispose = phin_slider_button_dispose;
+
+    widget_class->realize =             phin_slider_button_realize;
+    widget_class->unrealize =           phin_slider_button_unrealize;
+    widget_class->map =                 phin_slider_button_map;
+    widget_class->unmap =               phin_slider_button_unmap;
+    widget_class->size_allocate =       phin_slider_button_size_allocate;
+    widget_class->expose_event =        phin_slider_button_expose;
+    widget_class->button_press_event =  phin_slider_button_button_press;
+    widget_class->button_release_event= phin_slider_button_button_release;
+    widget_class->key_press_event =     phin_slider_button_key_press;
+    widget_class->scroll_event =        phin_slider_button_scroll;
+    widget_class->enter_notify_event =  phin_slider_button_enter_notify;
+    widget_class->leave_notify_event =  phin_slider_button_leave_notify;
+    widget_class->motion_notify_event = phin_slider_button_motion_notify;
+
+    /**
+     * PhinSliderButton::value-changed:
+     * @button: the object on which the signal was emitted
+     *
+     * The "value-changed" signal is emitted when the value of the
+     * button's adjustment changes.
+     *
+     */
+    signals[VALUE_CHANGED_SIGNAL] =
+        g_signal_new ("value-changed",
+                G_TYPE_FROM_CLASS (klass),
+                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                G_STRUCT_OFFSET (PhinSliderButtonClass, value_changed),
+                NULL, NULL,
+                g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    /**
+     * PhinSliderButton::changed:
+     * @button: the object on which the signal was emitted
+     *
+     * The "changed" signal is emitted when any parameter of the
+     * slider's adjustment changes, except for the %value parameter.
+     *
+     */
+    signals[CHANGED_SIGNAL] =
+        g_signal_new ("changed",
+                G_TYPE_FROM_CLASS (klass),
+                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                G_STRUCT_OFFSET (PhinSliderButtonClass, changed),
+                NULL, NULL,
+                g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    klass->value_changed = NULL;
+    klass->changed = NULL;
+}
+
+
+
+static void phin_slider_button_init (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    GtkBox* box = GTK_BOX (button);
+    GtkWidget* widget = GTK_WIDGET (button);
+    GtkContainer* container = GTK_CONTAINER (button);
+    int focus_width, focus_pad;
+    GtkStyle* style;
+
+    gtk_widget_set_can_focus(widget, TRUE);
+     
+    /* our parent class sets this to false; we need it to be true so
+     * that we are drawn without glitches when first shown
+     * (mapped) */
+    gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), TRUE);
+
+    p->arrow_cursor = NULL;
+    p->empty_cursor = NULL;
+    p->event_window = NULL;
+    p->left_arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+    p->right_arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+    p->label = gtk_label_new (NULL);
+    p->prefix_label = NULL;
+    p->postfix_label = NULL;
+    p->entry = gtk_entry_new ( );
+    p->adjustment = NULL;
+    p->prefix = NULL;
+    p->postfix = NULL;
+    p->digits = DIGITS;
+    p->hilite = 0;
+    p->state = STATE_NORMAL;
+    p->xpress = 0;
+    p->ypress = 0;
+    p->xpress_root = 0;
+    p->ypress_root = 0;
+    p->threshold = 3;
+    p->slid = FALSE;
+    p->firstrun = 1;
+     
+    gtk_box_pack_start (box, p->left_arrow, FALSE, FALSE, 0);
+    gtk_box_pack_start (box, p->label, TRUE, TRUE, 0);
+    gtk_box_pack_start (box, p->entry, TRUE, TRUE, 0);
+    gtk_box_pack_start (box, p->right_arrow, FALSE, FALSE, 0);
+
+    gtk_widget_style_get (widget,
+                          "focus-line-width",   &focus_width,
+                          "focus-padding",      &focus_pad,
+                          NULL);
+
+    style = gtk_widget_get_style(widget);
+
+    gtk_container_set_border_width (container,
+                MAX(    MAX(style->xthickness, style->ythickness),
+                        focus_width + focus_pad));
+
+    gtk_entry_set_has_frame (GTK_ENTRY (p->entry), FALSE);
+
+    gtk_entry_set_alignment (GTK_ENTRY (p->entry), 0.5);
+
+    g_signal_connect (G_OBJECT (p->entry), "activate",
+                      G_CALLBACK (phin_slider_button_entry_activate),
+                      (gpointer) button);
+
+    g_signal_connect (G_OBJECT (p->entry), "focus-out-event",
+                      G_CALLBACK (phin_slider_button_entry_focus_out),
+                      (gpointer) button);
+
+    g_signal_connect (G_OBJECT (p->entry), "key-press-event",
+                      G_CALLBACK (phin_slider_button_entry_key_press),
+                      (gpointer) button);
+
+    gtk_misc_set_alignment (GTK_MISC (p->left_arrow), 0.5, 0.5);
+    gtk_misc_set_alignment (GTK_MISC (p->right_arrow), 0.5, 0.5);
+}
+
+
+
+static void phin_slider_button_dispose (GObject* object)
+{
+    PhinSliderButtonPrivate* p;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (object));
+
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(object);
+
+    if (p->arrow_cursor)
+    {
+        gdk_cursor_unref (p->arrow_cursor);
+        p->arrow_cursor = NULL;
+    }
+
+    if (p->empty_cursor)
+    {
+        gdk_cursor_unref (p->empty_cursor);
+        p->empty_cursor = NULL;
+    }
+
+    if (p->event_window)
+    {
+        gdk_window_set_user_data (p->event_window, NULL);
+        gdk_window_destroy (p->event_window);
+        p->event_window = NULL;
+    }
+
+    if (p->left_arrow)
+    {
+        gtk_widget_destroy (p->left_arrow);
+        p->left_arrow = NULL;
+    }
+
+    if (p->right_arrow)
+    {
+        gtk_widget_destroy (p->right_arrow);
+        p->right_arrow = NULL;
+    }
+     
+    if (p->label)
+    {
+        gtk_widget_destroy (p->label);
+        p->label = NULL;
+    }
+
+    if (p->prefix_label)
+    {
+        gtk_widget_destroy (p->prefix_label);
+        p->prefix_label = NULL;
+    }
+
+    if (p->postfix_label)
+    {
+        gtk_widget_destroy (p->postfix_label);
+        p->postfix_label = NULL;
+    }
+
+    if (p->entry)
+    {
+        gtk_widget_destroy (p->entry);
+        p->entry = NULL;
+    }
+
+    if (p->adjustment)
+    {
+        g_signal_handlers_disconnect_by_func(p->adjustment,
+                            phin_slider_button_adjustment_changed,
+                            (gpointer) PHIN_SLIDER_BUTTON(object));
+
+        g_signal_handlers_disconnect_by_func(p->adjustment,
+                            phin_slider_button_adjustment_value_changed,
+                            (gpointer) PHIN_SLIDER_BUTTON(object));
+
+        g_object_unref (p->adjustment);
+        p->adjustment = NULL;
+    }
+
+    if (p->prefix)
+    {
+        g_free (p->prefix);
+        p->prefix = NULL;
+    }
+
+    if (p->postfix)
+    {
+        g_free (p->postfix);
+        p->postfix = NULL;
+    }
+
+    G_OBJECT_CLASS(phin_slider_button_parent_class)->dispose(object);
+}
+
+
+
+static void phin_slider_button_realize (GtkWidget* widget)
+{
+    PhinSliderButton*           button = PHIN_SLIDER_BUTTON(widget);
+    GtkWidgetClass*             klass;
+    PhinSliderButtonPrivate*    p;
+
+    GdkWindowAttr   attributes;
+    GtkAllocation   widget_alloc;
+    int             attributes_mask;
+
+    debug ("realize\n");
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (widget));
+
+    klass = GTK_WIDGET_CLASS(phin_slider_button_parent_class);
+
+    if (klass->realize)
+        klass->realize (widget);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.wclass      = GDK_INPUT_ONLY;
+    attributes.event_mask  = gtk_widget_get_events (widget);
+    attributes.event_mask |= (GDK_BUTTON_PRESS_MASK
+                              | GDK_BUTTON_RELEASE_MASK
+                              | GDK_POINTER_MOTION_MASK
+                              | GDK_POINTER_MOTION_HINT_MASK
+                              | GDK_ENTER_NOTIFY_MASK
+                              | GDK_LEAVE_NOTIFY_MASK
+                              | GDK_SCROLL_MASK
+                              | GDK_KEY_PRESS_MASK);
+
+    gtk_widget_get_allocation(widget, &widget_alloc);
+
+    attributes.x = widget_alloc.x;
+    attributes.y = widget_alloc.y;
+    attributes.width = widget_alloc.width;
+    attributes.height = widget_alloc.height;
+    attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    p->event_window = gdk_window_new(gtk_widget_get_parent_window(widget),
+                                           &attributes, attributes_mask);
+    gdk_window_set_user_data (p->event_window, widget);
+
+    p->arrow_cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
+    p->empty_cursor = gdk_cursor_new (GDK_BLANK_CURSOR);
+}
+
+
+
+static void phin_slider_button_unrealize (GtkWidget *widget)
+{
+    PhinSliderButton*           button = PHIN_SLIDER_BUTTON(widget);
+    GtkWidgetClass*             klass;
+    PhinSliderButtonPrivate*    p;
+
+    debug ("unrealize\n");
+
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    gdk_cursor_unref (p->arrow_cursor);
+    p->arrow_cursor = NULL;
+
+    gdk_cursor_unref (p->empty_cursor);
+    p->empty_cursor = NULL;
+
+    gdk_window_set_user_data (p->event_window, NULL);
+    gdk_window_destroy (p->event_window);
+    p->event_window = NULL;
+     
+    klass = GTK_WIDGET_CLASS(phin_slider_button_parent_class);
+
+    if (klass->unrealize)
+        klass->unrealize (widget);
+}
+
+
+static void phin_slider_button_map (GtkWidget *widget)
+{
+    PhinSliderButton*           button;
+    PhinSliderButtonPrivate*    p;
+
+    debug ("map\n");
+
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (widget));
+    button = PHIN_SLIDER_BUTTON(widget);
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    gtk_widget_show (p->left_arrow);
+    gtk_widget_show (p->label);
+    gtk_widget_show (p->right_arrow);
+    gdk_window_show (p->event_window);
+     
+    if (p->prefix_label)
+        gtk_widget_show (p->prefix_label);
+
+    if (p->postfix_label)
+        gtk_widget_show (p->postfix_label);
+
+    GTK_WIDGET_CLASS(phin_slider_button_parent_class)->map(widget);
+
+    gtk_widget_queue_draw(widget);
+}
+
+
+static void phin_slider_button_unmap (GtkWidget *widget)
+{
+    PhinSliderButton*           button;
+    PhinSliderButtonPrivate*    p;
+
+    debug ("unmap\n");
+
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (widget));
+    button = PHIN_SLIDER_BUTTON(widget);
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    gtk_widget_hide (p->left_arrow);
+    gtk_widget_hide (p->label);
+    gtk_widget_hide (p->right_arrow);
+    gdk_window_hide (p->event_window);
+     
+    if (p->prefix_label)
+        gtk_widget_hide (p->prefix_label);
+
+    if (p->postfix_label)
+        gtk_widget_hide (p->postfix_label);
+
+    GTK_WIDGET_CLASS(phin_slider_button_parent_class)->unmap(widget);
+}
+
+
+static void phin_slider_button_size_allocate (GtkWidget* widget,
+                                              GtkAllocation* allocation)
+{
+    PhinSliderButton*           button;
+    PhinSliderButtonPrivate*    p;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (allocation != NULL);
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (widget));
+
+    debug ("size allocate\n");
+
+    button = PHIN_SLIDER_BUTTON(widget);
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    GTK_WIDGET_CLASS(phin_slider_button_parent_class)
+                        ->size_allocate (widget, allocation);
+
+    if (gtk_widget_get_realized (widget))
+    {
+        gdk_window_move_resize (p->event_window,
+                                allocation->x,
+                                allocation->y,
+                                allocation->width,
+                                allocation->height);
+        /* make sure entry is hidden at start */
+        if(p->firstrun)
+        {
+            gtk_widget_hide(p->entry);
+            p->firstrun = 0;
+        }
+    }
+}
+
+
+static gboolean phin_slider_button_expose (GtkWidget*      widget,
+                                           GdkEventExpose* event)
+{
+    PhinSliderButton*           button;
+    PhinSliderButtonPrivate*    p;
+
+    /* <GRRRRRRRRRRRR> */
+    GtkAllocation a;
+    GtkAllocation arrow_a;
+    GtkAllocation label_a;
+    /* </GRRRRRRRRRRRR> */
+
+    GtkStyle* style;
+    GdkWindow* window;
+    int pad;                      /* pad */
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (PHIN_IS_SLIDER_BUTTON (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    g_return_val_if_fail (gtk_widget_is_drawable (widget), FALSE);
+    g_return_val_if_fail (event->count == 0, FALSE);
+
+    //debug ("expose\n");
+
+    button = PHIN_SLIDER_BUTTON(widget);
+    p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    gtk_widget_get_allocation(widget, &a);
+    gtk_widget_get_allocation(p->left_arrow, &arrow_a);
+    gtk_widget_get_allocation(p->label, &label_a);
+
+    style = gtk_widget_get_style(widget);
+    window = gtk_widget_get_window(widget);
+
+    pad = gtk_container_get_border_width (GTK_CONTAINER (widget));
+
+    /* clear the box */
+    gtk_paint_box (style, window,
+                   GTK_STATE_NORMAL,
+                   GTK_SHADOW_NONE,
+                   NULL,
+                   widget,
+                   NULL,        /* if this is "buttondefault" the
+                                 * smooth engine crashes */
+                   a.x, a.y, a.width, a.height);
+
+    /* paint any applicable hilites */
+    if (p->state == STATE_NORMAL)
+    {
+        if (p->hilite == LEFT_ARROW)
+        {
+            gtk_paint_box (style, window,
+                           GTK_STATE_PRELIGHT,
+                           GTK_SHADOW_NONE,
+                           NULL,
+                           widget,
+                           "button",
+                           a.x, a.y,
+                           arrow_a.width + pad,
+                           a.height);
+        }
+        else if (p->hilite == RIGHT_ARROW)
+        {
+            int offset;
+            int width;
+
+            offset = (a.x + arrow_a.width + label_a.width + pad);
+
+            width = a.x + a.width - offset;
+               
+            if (p->prefix_label)
+            {
+                GtkAllocation pre_a;
+                gtk_widget_get_allocation(p->prefix_label, &pre_a);
+                offset += pre_a.width;
+            }
+
+            if (p->postfix_label)
+            {
+                GtkAllocation post_a;
+                gtk_widget_get_allocation(p->postfix_label, &post_a);
+                offset += post_a.width;
+            }
+               
+            gtk_paint_box (style, window,
+                           GTK_STATE_PRELIGHT,
+                           GTK_SHADOW_NONE,
+                           NULL,
+                           widget, "button",
+                           offset, a.y, width, a.height);
+        }
+        else if (p->hilite == LABEL)
+        {
+            int offset;
+            int width;
+
+            offset = a.x + arrow_a.width + pad;
+
+            width = label_a.width;
+
+            if (p->prefix_label)
+            {
+                GtkAllocation pre_a;
+                gtk_widget_get_allocation(p->prefix_label, &pre_a);
+                width += pre_a.width;
+            }
+
+            if (p->postfix_label)
+            {
+                GtkAllocation post_a;
+                gtk_widget_get_allocation(p->prefix_label, &post_a);
+                width += post_a.width;
+            }
+
+            gtk_paint_box (style, window,
+                           GTK_STATE_PRELIGHT,
+                           GTK_SHADOW_NONE,
+                           NULL,
+                           widget, "button",
+                           offset, a.y, width, a.height);
+        }
+    }
+     
+    /* paint our border */
+    gtk_paint_shadow (style, window,
+                      GTK_STATE_NORMAL,
+                      GTK_SHADOW_OUT,
+                      NULL,
+                      widget,
+                      "buttondefault",
+                      a.x, a.y, a.width, a.height);
+
+    gtk_paint_shadow (style, window,
+                      GTK_STATE_NORMAL,
+                      GTK_SHADOW_OUT,
+                      NULL,
+                      widget,
+                      "button",
+                      a.x, a.y, a.width, a.height);
+
+    /* paint the focus if we have it */
+    if (gtk_widget_get_can_focus (widget))
+    {
+        int x, y;
+        int width, height;
+
+        x = a.x;
+        y = a.y;
+        width = a.width;
+        height = a.height;
+          
+        x += pad;
+        y += pad;
+        width -= 2 * pad;
+        height -= 2 * pad;
+
+        gtk_paint_focus (style, window, gtk_widget_get_state (widget),
+                         NULL, widget, "button",
+                         x, y, width, height);
+    }
+
+    GTK_WIDGET_CLASS(phin_slider_button_parent_class)
+                            ->expose_event (widget, event);
+     
+    return FALSE;
+}
+
+
+
+static void phin_slider_button_adjustment_changed (GtkAdjustment* adj,
+                                                   PhinSliderButton* button)
+{
+    (void)adj;
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (button));
+    update_size (button);
+    update_label (button);
+    g_signal_emit (G_OBJECT (button), signals[CHANGED_SIGNAL], 0);
+}
+
+
+static void phin_slider_button_adjustment_value_changed (GtkAdjustment* adj,
+                                                   PhinSliderButton* button)
+{
+    (void)adj;
+    g_return_if_fail (PHIN_IS_SLIDER_BUTTON (button));
+    update_label (button);
+    g_signal_emit (G_OBJECT (button), signals[VALUE_CHANGED_SIGNAL], 0);
+}
+
+
+static void phin_slider_button_entry_activate (GtkEntry* entry,
+                                               PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    double newval;
+
+    debug ("entry activate\n");
+     
+    p->state = STATE_NORMAL;
+     
+    newval = g_strtod (gtk_entry_get_text (GTK_ENTRY (entry)), NULL);
+
+    gtk_adjustment_set_value (p->adjustment, newval);
+
+    gtk_widget_hide (GTK_WIDGET (entry));
+    gtk_widget_show (p->label);
+    gtk_widget_queue_draw (GTK_WIDGET (button));
+}
+
+
+static gboolean phin_slider_button_entry_focus_out(GtkEntry* entry,
+                                                   GdkEventFocus* event,
+                                                   PhinSliderButton* button)
+{
+    (void)entry; (void)event;
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    GtkWidget* widget = GTK_WIDGET (button);
+
+    debug ("entry focus out\n");
+
+    entry_cancel (button);
+    p->hilite = 0;
+
+    /* check to see if the pointer wandered outside our toplevel; if
+     * it did, we grab the focus so that a widget has the focus when
+     * our toplevel gets the focus back */
+    if (gtk_widget_is_toplevel (widget))
+    {
+        GtkWidget* toplevel = gtk_widget_get_toplevel (widget);
+        GdkScreen* screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
+        GdkDisplay* display = gdk_screen_get_display (screen);
+        GdkWindow* curwindow =
+            gdk_display_get_window_at_pointer (display, NULL, NULL);
+
+        if (curwindow)
+        {
+            curwindow = gdk_window_get_toplevel (curwindow);
+            GdkWindow* topwin = gtk_widget_get_window(toplevel);
+
+            if (curwindow != topwin)
+            {
+                debug ("%p, %p\n", curwindow, topwin);
+                gtk_widget_grab_focus (widget);
+            }
+        }
+        else
+        {
+            gtk_widget_grab_focus (widget);
+        }
+    }
+
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_entry_key_press(GtkEntry* entry,
+                                                   GdkEventKey* event,
+                                                   PhinSliderButton* button)
+{
+    (void)entry;
+    debug ("entry key press\n");
+
+    if (event->keyval == GDK_Escape)
+    {
+        entry_cancel (button);
+        gtk_widget_grab_focus (GTK_WIDGET (button));
+    }
+
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_button_press (GtkWidget* widget,
+                                                 GdkEventButton* event)
+{
+    PhinSliderButton* button = PHIN_SLIDER_BUTTON (widget);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    debug ("button press\n");
+     
+    if (event->type != GDK_BUTTON_PRESS)
+        return FALSE;
+
+    if(event->button == 1)
+    {
+        double adj_value;
+        double step_inc;
+
+        switch (p->state)
+        {
+        case STATE_NORMAL:
+            p->xpress = event->x;
+            p->ypress = event->y;
+            p->xpress_root = event->x_root;
+            p->ypress_root = event->y_root;
+            p->slid = FALSE;
+            adj_value = gtk_adjustment_get_value(p->adjustment);
+            step_inc = gtk_adjustment_get_step_increment(p->adjustment);
+
+            if (p->hilite == LEFT_ARROW)
+            {
+                p->state = STATE_PRESSED;
+                gtk_adjustment_set_value(p->adjustment,
+                                            (adj_value - step_inc));
+            }
+            else if (p->hilite == RIGHT_ARROW)
+            {
+                p->state = STATE_PRESSED;
+                gtk_adjustment_set_value(p->adjustment,
+                                            (adj_value + step_inc));
+            }
+            else
+            {
+                p->state = STATE_SLIDE;
+            }
+            break;
+
+        case STATE_SCROLL:
+            p->state = STATE_NORMAL;
+            update_cursor (button);
+            break;
+
+        case STATE_ENTRY:
+            entry_cancel (button);
+            p->state = STATE_NORMAL;
+            update_cursor (button);
+            break;
+        }
+
+        update_cursor (button);
+        gtk_widget_grab_focus (widget);
+        gtk_widget_queue_draw (widget);
+    }
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_button_release (GtkWidget* widget,
+                                                   GdkEventButton* event)
+{
+    PhinSliderButton* button = PHIN_SLIDER_BUTTON (widget);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+
+    char* s;
+    char* t;
+
+    debug ("button release\n");
+    /* react to left button only */
+    if(event->button == 1)
+    {   
+        switch (p->state)
+        {
+        case STATE_SLIDE:
+            if (!p->slid)
+            {
+                double adj_value = gtk_adjustment_get_value(p->adjustment);
+                p->state = STATE_ENTRY;
+                s = value_to_string(button, adj_value);
+
+                for (t = s; *t == ' '; t++);
+                gtk_entry_set_text (GTK_ENTRY (p->entry), t);
+                g_free (s);
+                s = t = NULL;
+                                  
+                gtk_widget_hide (p->label);
+                if (p->prefix_label)
+                    gtk_widget_hide (p->prefix_label);
+                if (p->postfix_label)
+                    gtk_widget_hide (p->postfix_label);
+                   
+                gtk_widget_show (p->entry);
+
+                gtk_widget_grab_focus (p->entry);
+            }
+            else
+            {
+                p->state = STATE_NORMAL;
+                phin_warp_pointer (event->x_root, event->y_root,
+                                   p->xpress_root, p->ypress_root);
+                update_cursor (button);
+            }
+            break;
+        case STATE_PRESSED:
+            p->state = STATE_NORMAL;
+            update_cursor (button);
+            break;
+        }
+
+        gtk_widget_queue_draw (widget);
+    }
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_key_press (GtkWidget* widget,
+                                              GdkEventKey* event)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+    GtkAdjustment* adj = p->adjustment;
+    double val = gtk_adjustment_get_value(adj);
+
+    //debug ("key press\n");
+
+    switch (event->keyval)
+    {
+    case GDK_Up:
+        gtk_adjustment_set_value(adj,
+                        val + gtk_adjustment_get_step_increment(adj));
+        break;
+    case GDK_Down:
+        gtk_adjustment_set_value (adj,
+                        val - gtk_adjustment_get_step_increment(adj));
+        break;
+    case GDK_Page_Up:
+        gtk_adjustment_set_value (adj,
+                        val + gtk_adjustment_get_page_increment(adj));
+        break;
+    case GDK_Page_Down:
+        gtk_adjustment_set_value (adj,
+                        val - gtk_adjustment_get_page_increment(adj));
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+static gboolean phin_slider_button_scroll (GtkWidget* widget,
+                                           GdkEventScroll* event)
+{
+    PhinSliderButton* button = PHIN_SLIDER_BUTTON (widget);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+    GtkAdjustment* adj;
+    double val, inc;
+
+    //debug ("scroll\n");
+
+    if (p->state != STATE_NORMAL
+     && p->state != STATE_SCROLL)
+    {
+        return FALSE;
+    }
+
+    p->state = STATE_SCROLL;
+    update_cursor (button);
+
+    p->xpress_root = event->x_root;
+    p->ypress_root = event->y_root;
+    p->xpress = event->x;
+    p->ypress = event->y;
+
+    adj = p->adjustment;
+    val = gtk_adjustment_get_value(adj);
+    inc = gtk_adjustment_get_page_increment(adj);
+
+    if (event->direction == GDK_SCROLL_UP
+     || event->direction == GDK_SCROLL_RIGHT)
+    {
+        gtk_adjustment_set_value (adj, val + inc);
+    }
+    else
+        gtk_adjustment_set_value (adj, val - inc);
+
+    gtk_widget_grab_focus (widget);
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_enter_notify (GtkWidget* widget,
+                                                 GdkEventCrossing* event)
+{
+    PhinSliderButton* button = PHIN_SLIDER_BUTTON (widget);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+
+    int old = p->hilite;
+
+    p->hilite = check_pointer(button, event->x, event->y);
+
+    if (p->hilite != old)
+    {
+        update_cursor (button);
+        gtk_widget_queue_draw (widget);
+    }
+
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_leave_notify (GtkWidget* widget,
+                                                 GdkEventCrossing* event)
+{
+    (void)event;
+
+    PhinSliderButton* button = PHIN_SLIDER_BUTTON (widget);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+
+    p->hilite = 0;
+
+    if (p->state == STATE_SCROLL)
+        p->state = STATE_NORMAL;
+
+    update_cursor (button);
+    gtk_widget_queue_draw (widget);
+
+    return FALSE;
+}
+
+
+static gboolean phin_slider_button_motion_notify (GtkWidget* widget,
+                                                  GdkEventMotion* event)
+{
+    PhinSliderButton* button = PHIN_SLIDER_BUTTON (widget);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+    int old = p->hilite;
+    int xdiff = 0;
+    double inc;
+
+    p->hilite = check_pointer (button, event->x, event->y);
+
+    if (p->hilite != old)
+    {
+        update_cursor (button);
+        gtk_widget_queue_draw (widget);
+    }
+
+    xdiff = (event->x - p->xpress) / p->threshold;
+    old = p->state;
+
+    if (ABS (xdiff) >= 1)
+    {
+        switch (p->state)
+        {
+        case STATE_SLIDE:
+            p->slid = TRUE;
+
+            inc = gtk_adjustment_get_step_increment(p->adjustment) * xdiff;
+
+            gtk_adjustment_set_value (p->adjustment,
+                        gtk_adjustment_get_value(p->adjustment) + inc);
+
+            phin_warp_pointer (event->x_root, event->y_root,
+                               p->xpress_root, p->ypress_root);
+            break;
+
+        case STATE_PRESSED:
+            p->state = STATE_SLIDE;
+            p->xpress_root = event->x_root;
+            p->ypress_root = event->y_root;
+            p->xpress = event->x;
+            p->ypress = event->y;
+            update_cursor (button);
+            break;
+
+        case STATE_SCROLL:
+            p->state = STATE_NORMAL;
+            update_cursor (button);
+            break;
+        }
+    }
+    else if (p->state == STATE_SCROLL
+        && (ABS (event->x - p->xpress) >= SCROLL_THRESHOLD
+         || ABS (event->y - p->ypress) >= SCROLL_THRESHOLD))
+    {
+        p->state = STATE_NORMAL;
+        update_cursor (button);
+    }
+
+    if (p->state != old)
+        gtk_widget_queue_draw (widget);
+     
+    /* signal that we want more events */
+    gdk_window_get_pointer (NULL, NULL, NULL, NULL);
+     
+    return FALSE;
+}
+
+
+static int check_pointer (PhinSliderButton* button, int x, int y)
+{
+    GtkWidget* widget = GTK_WIDGET (button);
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(widget);
+
+    GtkAllocation a;
+    GtkAllocation la;
+    GtkAllocation ra;
+
+    int pad = gtk_container_get_border_width (GTK_CONTAINER (button));
+    int val;
+
+    gtk_widget_get_allocation(widget,         &a);
+    gtk_widget_get_allocation(p->left_arrow,  &la);
+    gtk_widget_get_allocation(p->right_arrow, &ra);
+
+    if ((y < 0 || y > a.height) || (x < 0 || x > a.width))
+    {
+        val = 0;
+    }
+    else if (x <= la.width + pad)
+    {
+        val = LEFT_ARROW;
+    }
+    else if (x >= (a.width - ra.width - pad))
+    {
+        val = RIGHT_ARROW;
+    }
+    else
+    {
+        val = LABEL;
+    }
+
+    return val;
+}     
+
+
+static void entry_cancel (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    p->state = STATE_NORMAL;
+
+    g_signal_handlers_block_by_func (p->entry,
+                                     phin_slider_button_entry_focus_out,
+                                     button);
+    gtk_widget_hide (p->entry);
+    g_signal_handlers_unblock_by_func (p->entry,
+                                       phin_slider_button_entry_focus_out,
+                                       button);
+    gtk_widget_show (p->label);
+
+    if (p->prefix_label)
+        gtk_widget_show (p->prefix_label);
+
+    if (p->postfix_label)
+        gtk_widget_show (p->postfix_label);
+
+    gtk_widget_queue_draw (GTK_WIDGET (button));
+}
+
+
+static void update_cursor (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+
+    switch (p->state)
+    {
+    case STATE_ENTRY:
+    case STATE_PRESSED:
+        gdk_window_set_cursor (p->event_window, NULL);
+        break;
+    case STATE_SLIDE:
+    case STATE_SCROLL:
+        gdk_window_set_cursor (p->event_window, p->empty_cursor);
+        break;
+    default:
+        if (p->hilite == LABEL)
+        {
+            gdk_window_set_cursor (p->event_window, p->arrow_cursor);
+        }
+        else
+        {
+            gdk_window_set_cursor (p->event_window, NULL);
+        }
+        break;
+    }
+}
+
+
+static void update_label (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    char* s;
+
+    s = value_to_string (button, gtk_adjustment_get_value(p->adjustment));
+
+    gtk_label_set_text (GTK_LABEL (p->label), s);
+    gtk_widget_queue_draw (GTK_WIDGET (button)); /* just to be safe */
+
+    g_free (s);
+}
+
+
+/* Update the sizes and existence of the labels we use, depending on
+ * whether p->prefix and/or p->postfix are set.
+ */
+static void update_size (PhinSliderButton* button)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    GtkRequisition req;
+    GString* s = g_string_new (NULL);
+    GString* t = g_string_new (NULL);
+    char* u;
+    int label_len = 0;
+    int prefix_len = 0;
+    int postfix_len = 0;
+    double prefix_frac = 0;
+    double postfix_frac = 0;
+    int width = 0;
+    int width_avail = 0;
+    int width_alloc = 0;
+
+    u = value_to_string(button, gtk_adjustment_get_lower(p->adjustment));
+    s = g_string_new(u);
+    g_free(u);
+
+    u = value_to_string(button, gtk_adjustment_get_upper(p->adjustment));
+    t = g_string_new(u);
+    g_free(u);
+
+    label_len = MAX (s->len, t->len);
+
+    if (p->prefix)
+        prefix_len = strlen (p->prefix);
+
+    if (p->postfix)
+        postfix_len = strlen (p->postfix);
+
+    width = label_len + prefix_len + postfix_len;
+    gtk_entry_set_width_chars (GTK_ENTRY (p->entry), width);
+
+    gtk_widget_size_request (p->entry, &req);
+
+    prefix_frac = prefix_len * 1.0 / width;
+    postfix_frac = postfix_len * 1.0 / width;
+    width_avail = req.width;
+
+    if (p->prefix)
+    {
+        if (!p->prefix_label)
+        {
+            p->prefix_label = gtk_label_new (NULL);
+            gtk_misc_set_alignment(GTK_MISC(p->prefix_label), 0.0, 0.5);
+            gtk_box_pack_start (GTK_BOX (button), p->prefix_label,
+                                TRUE, TRUE, 0);
+            gtk_box_reorder_child (GTK_BOX (button), p->prefix_label, 1);
+
+            if (gtk_widget_get_mapped (GTK_WIDGET (button)))
+                gtk_widget_show (p->prefix_label);
+        }
+
+        gtk_label_set_text (GTK_LABEL (p->prefix_label),
+                            p->prefix);
+
+        width_alloc = req.width * prefix_frac;
+        gtk_widget_set_size_request (p->prefix_label,
+                                     width_alloc,
+                                     req.height);
+        width_avail -= width_alloc;
+    }
+    else if (p->prefix_label)
+    {
+        gtk_widget_destroy (p->prefix_label);
+        p->prefix_label = NULL;
+    }
+
+    if (p->postfix)
+    {
+        if (!p->postfix_label)
+        {
+            p->postfix_label = gtk_label_new (NULL);
+            gtk_misc_set_alignment(GTK_MISC(p->postfix_label), 1.0, 0.5);
+            gtk_box_pack_start (GTK_BOX (button), p->postfix_label,
+                                TRUE, TRUE, 0);
+            gtk_box_reorder_child (GTK_BOX (button), p->postfix_label,
+                                   (p->prefix_label)? 3: 2);
+
+            if (gtk_widget_get_mapped (GTK_WIDGET (button)))
+                gtk_widget_show (p->postfix_label);
+        }
+
+        gtk_label_set_text (GTK_LABEL (p->postfix_label),
+                            p->postfix);
+
+        width_alloc = req.width * postfix_frac;
+        gtk_widget_set_size_request (p->postfix_label,
+                                     width_alloc,
+                                     req.height);
+        width_avail -= width_alloc;
+    }
+    else if (p->postfix_label)
+    {
+        gtk_widget_destroy (p->postfix_label);
+        p->postfix_label = NULL;
+    }
+
+    /* we allocate the remainder to avoid floating point errors */
+    gtk_widget_set_size_request (p->label,
+                                 width_avail,
+                                 req.height);
+    gtk_widget_queue_draw (GTK_WIDGET (button));
+
+    g_string_free (s, TRUE);
+    g_string_free (t, TRUE);
+}
+
+
+static char* value_to_string(PhinSliderButton* button, double value)
+{
+    PhinSliderButtonPrivate* p = PHIN_SLIDER_BUTTON_GET_PRIVATE(button);
+    return g_strdup_printf ("%.*f", p->digits, value);
+}
diff --git a/libphin/phinsliderbutton.h b/libphin/phinsliderbutton.h
new file mode 100644
index 0000000..c6283cd
--- /dev/null
+++ b/libphin/phinsliderbutton.h
@@ -0,0 +1,105 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_SLIDER_BUTTON_H__
+#define __PHIN_SLIDER_BUTTON_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define PHIN_TYPE_SLIDER_BUTTON         (phin_slider_button_get_type())
+
+#define PHIN_SLIDER_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                        PHIN_TYPE_SLIDER_BUTTON, \
+                                        PhinSliderButton))
+
+#define PHIN_SLIDER_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                        PHIN_TYPE_SLIDER_BUTTON, \
+                                        PhinSliderButtonClass))
+
+#define PHIN_IS_SLIDER_BUTTON(obj)      (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                        PHIN_TYPE_SLIDER_BUTTON))
+
+#define PHIN_IS_SLIDER_BUTTON_CLASS(klass)\
+    (G_TYPE_CHECK_CLASS_TYPE ((klass),  PHIN_TYPE_SLIDER_BUTTON))
+
+
+typedef struct _PhinSliderButtonClass PhinSliderButtonClass;
+typedef struct _PhinSliderButton      PhinSliderButton;
+
+
+struct _PhinSliderButton
+{
+    GtkHBox parent;
+};
+
+struct _PhinSliderButtonClass
+{
+    GtkHBoxClass parent_class;
+
+    void (*value_changed) (PhinSliderButton* slider);
+    void (*changed)       (PhinSliderButton* slider);
+};
+
+
+GType       phin_slider_button_get_type ( );
+GtkWidget*  phin_slider_button_new (GtkAdjustment*, int digits);
+GtkWidget*  phin_slider_button_new_with_range ( double value,
+                                                double lower,
+                                                double upper,
+                                                double step,
+                                                int digits);
+
+void    phin_slider_button_set_value(   PhinSliderButton*, double );
+double  phin_slider_button_get_value (  PhinSliderButton* );
+
+void    phin_slider_button_set_range (  PhinSliderButton*,
+                                        double lower,   double upper);
+void    phin_slider_button_get_range (  PhinSliderButton*,
+                                        double* lower,  double* upper);
+
+void            phin_slider_button_set_adjustment(  PhinSliderButton*,
+                                                    GtkAdjustment* );
+GtkAdjustment*  phin_slider_button_get_adjustment (PhinSliderButton* ); 
+
+void    phin_slider_button_set_increment(   PhinSliderButton*,
+                                            double step, double page);
+void    phin_slider_button_get_increment(   PhinSliderButton*,
+                                            double* step, double* page);
+
+void    phin_slider_button_set_format(  PhinSliderButton*,  int digits,
+                                        const char* prefix,
+                                        const char* postfix);
+void    phin_slider_button_get_format(  PhinSliderButton*,  int* digits,
+                                        char** prefix,
+                                        char** postfix);
+
+void    phin_slider_button_set_threshold(   PhinSliderButton*,
+                                            guint threshold);
+int     phin_slider_button_get_threshold(   PhinSliderButton*);
+
+
+G_END_DECLS
+
+#endif /* __PHIN_SLIDER_BUTTON_H__ */
diff --git a/libphin/phinvfanslider.c b/libphin/phinvfanslider.c
new file mode 100644
index 0000000..4ccfb5d
--- /dev/null
+++ b/libphin/phinvfanslider.c
@@ -0,0 +1,79 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <gtk/gtk.h>
+
+
+#include "phinprivate.h"
+#include "phinfanslider.h"
+#include "phinvfanslider.h"
+
+
+G_DEFINE_TYPE(PhinVFanSlider, phin_vfan_slider, PHIN_TYPE_FAN_SLIDER);
+
+
+GtkWidget* phin_vfan_slider_new (GtkAdjustment* adjustment)
+{
+    PhinVFanSlider* slider;
+
+    int adj_lower = gtk_adjustment_get_lower(adjustment);
+    int adj_upper = gtk_adjustment_get_upper(adjustment);
+    int adj_value = gtk_adjustment_get_value(adjustment);
+
+    g_assert (adj_lower < adj_upper);
+    g_assert((adj_value >= adj_lower)
+          && (adj_value <= adj_upper));
+
+    slider = g_object_new (PHIN_TYPE_VFAN_SLIDER, NULL);
+
+    phin_fan_slider_set_orientation(PHIN_FAN_SLIDER(slider),
+                                    GTK_ORIENTATION_VERTICAL);
+
+    phin_fan_slider_set_adjustment (PHIN_FAN_SLIDER (slider), adjustment);
+
+    return (GtkWidget*) slider;
+}
+
+
+GtkWidget* phin_vfan_slider_new_with_range (double value, double lower,
+                                            double upper, double step)
+{
+    GtkAdjustment* adj;
+
+    adj = (GtkAdjustment*)gtk_adjustment_new (value, lower, upper,
+                                                    step, step, 0);
+    return phin_vfan_slider_new (adj);
+}
+
+
+static void phin_vfan_slider_class_init (PhinVFanSliderClass* klass)
+{
+    phin_vfan_slider_parent_class = g_type_class_peek_parent(klass);
+}
+
+
+static void phin_vfan_slider_init(PhinVFanSlider* slider)
+{
+    (void)slider;
+    return;
+}
diff --git a/libphin/phinvfanslider.h b/libphin/phinvfanslider.h
new file mode 100644
index 0000000..42f6d1c
--- /dev/null
+++ b/libphin/phinvfanslider.h
@@ -0,0 +1,75 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_VFAN_SLIDER_H__
+#define __PHIN_VFAN_SLIDER_H__
+
+#include <gtk/gtk.h>
+
+#include "phinfanslider.h"
+
+
+G_BEGIN_DECLS
+
+#define PHIN_TYPE_VFAN_SLIDER           (phin_vfan_slider_get_type())
+
+#define PHIN_VFAN_SLIDER(obj)           (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                        PHIN_TYPE_VFAN_SLIDER, \
+                                        PhinVFanSlider))
+
+#define PHIN_VFAN_SLIDER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                        PHIN_TYPE_VFAN_SLIDER, \
+                                        PhinVFanSliderClass))
+
+#define PHIN_IS_VFAN_SLIDER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                        PHIN_TYPE_VFAN_SLIDER))
+
+#define PHIN_IS_VFAN_SLIDER_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE ((klass),  PHIN_TYPE_VFAN_SLIDER))
+
+
+typedef struct _PhinVFanSliderClass PhinVFanSliderClass;
+typedef struct _PhinVFanSlider      PhinVFanSlider;
+
+
+struct _PhinVFanSlider
+{
+    PhinFanSlider parent;
+};
+
+struct _PhinVFanSliderClass
+{
+    PhinFanSliderClass parent_class;
+};
+
+
+GType       phin_vfan_slider_get_type(void);
+GtkWidget*  phin_vfan_slider_new (GtkAdjustment*);
+GtkWidget*  phin_vfan_slider_new_with_range(double value,
+                                            double lower,
+                                            double upper,
+                                            double step);
+
+G_END_DECLS
+
+#endif /* __PHIN_VFAN_SLIDER_H__ */
diff --git a/libphin/phinvkeyboard.c b/libphin/phinvkeyboard.c
new file mode 100644
index 0000000..d7c7f11
--- /dev/null
+++ b/libphin/phinvkeyboard.c
@@ -0,0 +1,67 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#include <gtk/gtk.h>
+#include "phinvkeyboard.h"
+
+
+G_DEFINE_TYPE(PhinVKeyboard, phin_vkeyboard, PHIN_TYPE_KEYBOARD);
+
+
+static void phin_vkeyboard_class_init(PhinVKeyboardClass* klass)
+{
+    phin_vkeyboard_parent_class = g_type_class_peek_parent(klass);
+}
+
+
+static void phin_vkeyboard_init(PhinVKeyboard* self)
+{
+    (void)self;
+}
+
+
+/**
+ * phin_vkeyboard_new:
+ * @adjustment: the #GtkAdjustment that the new keyboard will use for scrolling
+ * @numkeys: number of keys to create
+ * @show_labels: whether to label the C keys
+ *
+ * Creates a new #PhinVKeyboard.
+ *
+ * Returns: a newly created #PhinVKeyboard
+ * 
+ */
+GtkWidget* phin_vkeyboard_new(GtkAdjustment* adj,   int numkeys,
+                                                    gboolean show_labels)
+{
+    if (!adj)
+        adj = (GtkAdjustment*) gtk_adjustment_new(0, 0, 0, 0, 0, 0);
+
+    return g_object_new(PHIN_TYPE_VKEYBOARD,
+                        "vadjustment",  adj,
+                        "shadow-type",  GTK_SHADOW_NONE,
+                        "orientation",  GTK_ORIENTATION_VERTICAL,
+                        "numkeys",      numkeys,
+                        "show-labels",  show_labels,
+                        NULL);
+}
diff --git a/libphin/phinvkeyboard.h b/libphin/phinvkeyboard.h
new file mode 100644
index 0000000..f2a0d63
--- /dev/null
+++ b/libphin/phinvkeyboard.h
@@ -0,0 +1,72 @@
+/*  Phin is a fork of the PHAT Audio Toolkit.
+    Phin is part of Petri-Foo. Petri-Foo is a fork of Specimen.
+
+    Original author Pete Bessman
+    Copyright 2005 Pete Bessman
+    Copyright 2011 James W. Morris
+
+    This file is part of Phin.
+
+    Phin is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+
+    Phin 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 Phin.  If not, see <http://www.gnu.org/licenses/>.
+
+    This file is a derivative of a PHAT original, modified 2011
+*/
+#ifndef __PHIN_VKEYBOARD__
+#define __PHIN_VKEYBOARD__
+
+#include <gtk/gtk.h>
+
+#include "phinkeyboard.h"
+
+G_BEGIN_DECLS
+
+#define PHIN_TYPE_VKEYBOARD         (phin_vkeyboard_get_type())
+
+#define PHIN_VKEYBOARD(obj)         (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+                                    PHIN_TYPE_VKEYBOARD, PhinVKeyboard))
+
+#define PHIN_VKEYBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+                                    PHIN_TYPE_VKEYBOARD, \
+                                    PhinVKeyboardClass))
+
+#define PHIN_IS_VKEYBOARD(obj)      (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+                                    PHIN_TYPE_VKEYBOARD))
+
+#define PHIN_IS_VKEYBOARD_CLASS(klass) \
+    (G_TYPE_CHECK_INSTANCE_TYPE ((klass), PHIN_TYPE_VKEYBOARD))
+
+
+typedef struct _PhinVKeyboardClass  PhinVKeyboardClass;
+typedef struct _PhinVKeyboard       PhinVKeyboard;
+
+struct _PhinVKeyboard
+{
+    PhinKeyboard parent;
+};
+
+
+struct _PhinVKeyboardClass
+{
+    PhinKeyboardClass parent_class;
+};
+
+
+GType       phin_vkeyboard_get_type(void);
+GtkWidget*  phin_vkeyboard_new(GtkAdjustment*,  int numkeys,
+                                                gboolean show_labels);
+
+
+G_END_DECLS
+
+
+#endif /* __PHIN_VKEYBOARD__ */
diff --git a/petri-foo.desktop b/petri-foo.desktop
index aed78e3..eb002e3 100644
--- a/petri-foo.desktop
+++ b/petri-foo.desktop
@@ -4,8 +4,10 @@ Type=Application
 Name=Petri-Foo
 GenericName=Petri-Foo
 Comment=Sound Sampler
-Icon=petri-foo-small.png
+Comment[ru]=Звуковой сэмплер Petri-Foo
+Icon=petri-foo
 TryExec=petri-foo
 Exec=petri-foo
 Terminal=false
-Categories=Application;AudioVideo;AudioVideoEditing;Music;
+Categories=Application;Audio;AudioVideo;AudioVideoEditing;X-Jack;Midi;X-Alsa;
+MimeType=application/petri-foo;
diff --git a/petri-foo.xml b/petri-foo.xml
new file mode 100644
index 0000000..5cfe309
--- /dev/null
+++ b/petri-foo.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+  <mime-type type="application/x-petri-foo">
+  <comment>Petri-Foo sample bank file</comment>
+  <glob pattern="*.petri-foo"/>
+  </mime-type>
+</mime-info>
diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am
deleted file mode 100644
index 527b9cb..0000000
--- a/pixmaps/Makefile.am
+++ /dev/null
@@ -1,4 +0,0 @@
-pixmaps_DATA = open.png play.png stop.png panic.png \
-    petri-foo.png petri-foo_small.png
-EXTRA_DIST = $(pixmaps_DATA)
-pixmapsdir = "$(pkgdatadir)/pixmaps"
diff --git a/pixmaps/open.png b/pixmaps/open.png
deleted file mode 100644
index fbf04f0..0000000
Binary files a/pixmaps/open.png and /dev/null differ
diff --git a/pixmaps/panic.png b/pixmaps/panic.png
deleted file mode 100644
index bc9bfde..0000000
Binary files a/pixmaps/panic.png and /dev/null differ
diff --git a/pixmaps/play.png b/pixmaps/play.png
deleted file mode 100644
index 73d5e5f..0000000
Binary files a/pixmaps/play.png and /dev/null differ
diff --git a/pixmaps/stop.png b/pixmaps/stop.png
deleted file mode 100644
index 83c9caa..0000000
Binary files a/pixmaps/stop.png and /dev/null differ
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index d75c63a..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,47 +0,0 @@
-SUBDIRS = gui patch_private
-
-bin_PROGRAMS = petri-foo
-
-petri_foo_SOURCES = 					\
-	adsr.c			adsr.h			\
-				config.h		\
-	dish_file.c		dish_file.h		\
-	driver.c		driver.h		\
-	instance.c		instance.h		\
-	jackdriver.c		jackdriver.h		\
-	lfo.c			lfo.h			\
-	maths.c			maths.h			\
-	midi.c			midi.h			\
-				midi_control.h		\
-	mixer.c			mixer.h			\
-	mod_src.c		mod_src.h		\
-	names.c			names.h			\
-	patch.c			patch.h			\
-	patch_set_and_get.c	patch_set_and_get.h	\
-	patch_util.c		patch_util.h		\
-	petri-foo.c		petri-foo.h		\
-	sample.c		sample.h		\
-	sync.c			sync.h			\
-	ticks.c			ticks.h
-                                                                                                                                                                        
-INCLUDES = 			\
-	@ALSA_CFLAGS@ 		\
-	@JACK_CFLAGS@ 		\
-	@LIBSAMPLERATE_CFLAGS@ 	\
-	@LIBSNDFILE_CFLAGS@ 	\
-	@LIBXML_CFLAGS@ 	\
-	@GTK_CFLAGS@
-
-petri_foo_LDADD = 		\
-	patch_private/libpatch.a\
-	gui/libgui.a 		\
-	@PHAT_LIBS@ 		\
-	@ALSA_LIBS@ 		\
-	@JACK_LIBS@ 		\
-	@LIBSAMPLERATE_LIBS@ 	\
-	@LIBSNDFILE_LIBS@ 	\
-	@LIBXML_LIBS@ 		\
-	@GTK_LIBS@ 		\
-	@LIBGNOMECANVAS_LIBS@
-	
-EXTRA_DIST = gprof-helper.c
diff --git a/src/gprof-helper.c b/src/gprof-helper.c
deleted file mode 100644
index 031a108..0000000
--- a/src/gprof-helper.c
+++ /dev/null
@@ -1,139 +0,0 @@
-/*  Petri-Foo is a fork of the Specimen audio sampler.
-
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
-    Copyright 2011 James W. Morris
-
-    This file is part of Petri-Foo.
-
-    Petri-Foo is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2 as
-    published by the Free Software Foundation.
-
-    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
-*/
-
-
-/* gprof-helper.c -- preload library to profile pthread-enabled programs
- *
- * Authors: Sam Hocevar <sam at zoy dot org>
- *          Daniel Jönsson <danieljo at fagotten dot org>
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the Do What The Fuck You Want To
- *  Public License as published by Banlu Kemiyatorn. See
- *  http://sam.zoy.org/projects/COPYING.WTFPL for more details.
- *
- * Compilation example:
- * gcc -shared -nostdlib -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl
- *
- * Usage example:
- * LD_PRELOAD=./gprof-helper.so your_program
- */
-
-#define _GNU_SOURCE
-#include <sys/time.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <dlfcn.h>
-#include <pthread.h>
-
-static void *wrapper_routine (void *);
-
-/* Original pthread function */
-static int (*pthread_create_orig) (pthread_t * __restrict,
-				   __const pthread_attr_t * __restrict,
-				   void *(*)(void *),
-				   void *__restrict) = NULL;
-
-/* Library initialization function */
-void _init (void)
-{
-     pthread_create_orig = dlsym (RTLD_NEXT, "pthread_create");
-     fprintf (stderr, "pthreads: using profiling hooks for gprof\n");
-     if (pthread_create_orig == NULL)
-     {
-	  char *error = dlerror ( );
-
-	  if (error == NULL)
-	  {
-	       error = "pthread_create is NULL";
-	  }
-	  fprintf (stderr, "%s", error);
-	  exit (EXIT_FAILURE);
-     }
-}
-
-/* Our data structure passed to the wrapper */
-typedef struct wrapper_s
-{
-     void *(*start_routine) (void *);
-     void *arg;
-
-     pthread_mutex_t lock;
-     pthread_cond_t wait;
-
-     struct itimerval itimer;
-
-} wrapper_t;
-
-/* The wrapper function in charge for setting the itimer value */
-static void *wrapper_routine (void *data)
-{
-     /* Put user data in thread-local variables */
-     void *(*start_routine) (void *) = ((wrapper_t *) data)->start_routine;
-     void *arg = ((wrapper_t *) data)->arg;
-
-     /* Set the profile timer value */
-     setitimer (ITIMER_PROF, &((wrapper_t *) data)->itimer, NULL);
-
-     /* Tell the calling thread that we don't need its data anymore */
-     pthread_mutex_lock (&((wrapper_t *) data)->lock);
-     pthread_cond_signal (&((wrapper_t *) data)->wait);
-     pthread_mutex_unlock (&((wrapper_t *) data)->lock);
-
-     /* Call the real function */
-     return start_routine (arg);
-}
-
-/* Our wrapper function for the real pthread_create( ) */
-int pthread_create (pthread_t * __restrict thread,
-		    __const pthread_attr_t * __restrict attr,
-		    void *(*start_routine) (void *), void *__restrict arg)
-{
-     wrapper_t wrapper_data;
-     int i_return;
-
-     /* Initialize the wrapper structure */
-     wrapper_data.start_routine = start_routine;
-     wrapper_data.arg = arg;
-     getitimer (ITIMER_PROF, &wrapper_data.itimer);
-     pthread_cond_init (&wrapper_data.wait, NULL);
-     pthread_mutex_init (&wrapper_data.lock, NULL);
-     pthread_mutex_lock (&wrapper_data.lock);
-
-     /* The real pthread_create call */
-     i_return = pthread_create_orig (thread,
-				     attr, &wrapper_routine, &wrapper_data);
-
-     /* If the thread was successfully spawned, wait for the data
-      * to be released */
-     if (i_return == 0)
-     {
-	  pthread_cond_wait (&wrapper_data.wait, &wrapper_data.lock);
-     }
-
-     pthread_mutex_unlock (&wrapper_data.lock);
-     pthread_mutex_destroy (&wrapper_data.lock);
-     pthread_cond_destroy (&wrapper_data.wait);
-
-     return i_return;
-}
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
deleted file mode 100644
index c6db4b7..0000000
--- a/src/gui/Makefile.am
+++ /dev/null
@@ -1,31 +0,0 @@
-noinst_LIBRARIES = libgui.a
-
-libgui_a_SOURCES = 					\
-	audio-settings.c	audio-settings.h	\
-	bank-ops.c		bank-ops.h		\
-	basic_combos.c		basic_combos.h		\
-	channelsection.c	channelsection.h	\
-	envelopetab.c		envelopetab.h		\
-	gui.c			gui.h			\
-	idselector.c		idselector.h		\
-	lfotab.c		lfotab.h		\
-	mastersection.c		mastersection.h		\
-	midisection.c		midisection.h		\
-	mod_section.c		mod_section.h		\
-	mod_src_gui.c		mod_src_gui.h		\
-	paramtab.c		paramtab.h		\
-	patchlist.c		patchlist.h		\
-	patchsection.c		patchsection.h		\
-	sample-editor.c		sample-editor.h		\
-	sample-selector.c	sample-selector.h	\
-	sampletab.c		sampletab.h		\
-	voicetab.c		voicetab.h		\
-	waveform.c		waveform.h
-
-INCLUDES = 			\
-	-l.. 			\
-	@GTK_CFLAGS@ 		\
-	@LIBGNOMECANVAS_CFLAGS@ \
-	@PHAT_CFLAGS@
-
-libgui_a_CFLAGS = $(CFLAGS) -I.. -DINSTALLDIR=\"$(datadir)\"
diff --git a/src/gui/channelsection.c b/src/gui/channelsection.c
deleted file mode 100644
index d69ccc5..0000000
--- a/src/gui/channelsection.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*  Petri-Foo is a fork of the Specimen audio sampler.
-
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
-    Copyright 2011 James W. Morris
-
-    This file is part of Petri-Foo.
-
-    Petri-Foo is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2 as
-    published by the Free Software Foundation.
-
-    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
-*/
-
-
-#include <gtk/gtk.h>
-#include <phat/phat.h>
-#include "channelsection.h"
-#include "gui.h"
-#include "patchlist.h"
-#include "midi.h"
-#include "patch_set_and_get.h"
-
-
-
-G_DEFINE_TYPE(ChannelSection, channel_section, GTK_TYPE_VBOX)
-
-
-static void channel_section_class_init(ChannelSectionClass* klass)
-{
-    channel_section_parent_class = g_type_class_peek_parent(klass);
-}
-
-
-static void channel_cb(PhatSliderButton* button, ChannelSection* self)
-{
-    int channel = (int)phat_slider_button_get_value(button);
-    PatchList* list = gui_get_patch_list();
-
-    patch_set_channel(self->patch, channel-1);
-    patch_list_update(list, patch_list_get_current_patch(list),
-                                                PATCH_LIST_PATCH);
-}
-
-
-static void connect(ChannelSection* self)
-{
-    g_signal_connect(G_OBJECT(self->chan_sb), "value-changed",
-                        G_CALLBACK(channel_cb), (gpointer) self);
-}
-
-
-static void channel_section_init(ChannelSection* self)
-{
-    GtkBox* box = GTK_BOX(self);
-    GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
-    GtkBox* h = GTK_BOX(hbox);
-
-    self->patch = -1;
-
-    gui_pack(box, hbox);
-    gui_label_pack("Channel:", h);
-    gui_pack(h, gui_hpad_new(GUI_TEXTSPACE));
-
-    /* channel sliderbutton */
-    self->chan_sb = phat_slider_button_new_with_range(1, 1, MIDI_CHANS,1,0);
-    phat_slider_button_set_threshold(PHAT_SLIDER_BUTTON(self->chan_sb),
-                                                        GUI_THRESHOLD);
-    gui_pack(h, self->chan_sb);
-
-    /* done */
-    connect(self);
-}
-
-
-static void block(ChannelSection* self)
-{
-    g_signal_handlers_block_by_func(self->chan_sb, channel_cb, self);
-}
-
-
-static void unblock(ChannelSection* self)
-{
-    g_signal_handlers_unblock_by_func(self->chan_sb, channel_cb, self);
-}
-
-
-static void set_sensitive(ChannelSection* self, gboolean val)
-{
-    gtk_widget_set_sensitive(self->chan_sb, val);
-}
-
-
-GtkWidget* channel_section_new(void)
-{
-    return (GtkWidget*) g_object_new(CHANNEL_SECTION_TYPE, NULL);
-}
-
-
-void channel_section_set_patch(ChannelSection* self, int patch)
-{
-    int channel;
-
-    self->patch = patch;
-
-    if (patch < 0)
-        set_sensitive(self, FALSE);
-    else
-    {
-        set_sensitive(self, TRUE);
-        channel = patch_get_channel(patch);
-        block(self);
-        phat_slider_button_set_value(PHAT_SLIDER_BUTTON(self->chan_sb),
-                                                                channel+1);
-        unblock(self);
-    }
-}
-
-
-int channel_section_get_channel(ChannelSection* self)
-{
-    return (self->patch < 0) ? 0 : patch_get_channel(self->patch);
-}
diff --git a/src/gui/sample-editor.c b/src/gui/sample-editor.c
deleted file mode 100644
index 1d1298d..0000000
--- a/src/gui/sample-editor.c
+++ /dev/null
@@ -1,568 +0,0 @@
-/*  Petri-Foo is a fork of the Specimen audio sampler.
-
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
-    Copyright 2011 James W. Morris
-
-    This file is part of Petri-Foo.
-
-    Petri-Foo is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2 as
-    published by the Free Software Foundation.
-
-    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
-*/
-
-
-#include <stdio.h>
-#include <string.h>
-#include <gtk/gtk.h>
-#include "petri-foo.h"
-#include "patch_set_and_get.h"
-#include "waveform.h"
-#include "gui.h"
-#include "mixer.h"
-#include "sample-editor.h"
-
-#include "basic_combos.h"
-
-static GtkWidget* window;
-static GtkWidget* waveform;
-static GtkWidget* hscroll;
-
-static GtkObject* hscrolladj;
-
-static GtkWidget* wf_thumb = 0;
-
-static GtkWidget* fade_spin;
-static GtkWidget* xfade_spin;
-
-enum
-{
-     ZOOM_MIN = 1,
-     ZOOM_MAX = 100
-};
-
-
-static int ignore_callback = 0;
-
-static gboolean ignore_mark_change = FALSE; /* once upon a time*/
-
-static int zoom = ZOOM_MIN;
-static float range = 1.0;
-
-static int old_play_start, old_play_stop;
-static int old_loop_start, old_loop_stop;
-static int old_fade,       old_xfade;
-static int patch;
-
-static GtkWidget*   mark_combo;
-static GtkWidget*   mark_spin;
-static GtkWidget*   mark_val;
-
-
-static void update_mark_spin(void);
-static void update_fade_spins(void);
-static void cb_mark_combo_changed(GtkWidget* combo, gpointer data);
-
-static void cb_close (GtkWidget * widget, gpointer data)
-{
-    (void)widget;(void)data;
-    debug ("Hiding sample editor\n");
-    mixer_note_off_with_id (patch, patch_get_note (patch));
-    gtk_widget_hide (window);
-}
-
-static void cb_play (GtkWidget * widget, gpointer data)
-{
-    (void)widget;(void)data;
-     mixer_note_off_with_id (patch, patch_get_note (patch));
-     mixer_note_on_with_id (patch, patch_get_note (patch), 1.0);
-}
-
-static void cb_stop (GtkWidget * widget, gpointer data)
-{
-    (void)widget;(void)data;
-     mixer_note_off_with_id (patch, patch_get_note (patch));
-}
-
-static void cb_reset (GtkWidget * widget, gpointer data)
-{
-    (void)widget;(void)data;
-    debug ("Restoring initial values\n");
-
-    patch_set_mark_frame(patch, WF_MARK_PLAY_START, old_play_start);
-    patch_set_mark_frame(patch, WF_MARK_PLAY_STOP,  old_play_stop);
-    patch_set_mark_frame(patch, WF_MARK_LOOP_START, old_loop_start);
-    patch_set_mark_frame(patch, WF_MARK_LOOP_STOP,  old_loop_stop);
-    cb_mark_combo_changed(mark_combo, 0);
-    gtk_widget_queue_draw(waveform);
-    gtk_widget_queue_draw(wf_thumb);
-}
-
-static void cb_clear (GtkWidget * widget, gpointer data)
-{
-    (void)widget;
-    char *op = data;
-
-    if (strcmp(op, "loop") == 0)
-    {
-        int play_start = patch_get_mark_frame(patch, WF_MARK_PLAY_START);
-        int play_stop =  patch_get_mark_frame(patch, WF_MARK_PLAY_STOP);
-        patch_set_mark_frame(patch, WF_MARK_LOOP_START, play_start);
-        patch_set_mark_frame(patch, WF_MARK_LOOP_STOP, play_stop);
-     }
-     else if (strcmp(op, "play") == 0)
-     {
-        int frames = patch_get_mark_frame(patch, WF_MARK_STOP);
-        patch_set_mark_frame(patch, WF_MARK_PLAY_START, 0);
-        patch_set_mark_frame(patch, WF_MARK_PLAY_STOP, frames - 1);
-     }
-
-    cb_mark_combo_changed(mark_combo, 0);
-    gtk_widget_queue_draw(waveform);
-    gtk_widget_queue_draw(wf_thumb);
-}
-
-
-static void update_mark_val(int val)
-{
-    const float* wav = patch_get_sample(patch);
-    char buf[40];
-    snprintf(buf, 40, "%2.6f", wav[val * 2]);
-    gtk_label_set_label(GTK_LABEL(mark_val), buf);
-}
-
-
-static void cb_scroll (GtkWidget * scroll, gpointer data)
-{
-    (void)data;
-    float val = gtk_range_get_value(GTK_RANGE(scroll));
-    debug("scroll changing:%f\n",(float)val);
-    waveform_set_range(WAVEFORM(waveform), val, val + range);
-}
-
-
-static void cb_mark_spin_changed(GtkWidget * spin, gpointer data)
-{
-    debug("spin changing\n");
-    int val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mark_spin));
-    int mark = waveform_get_mark(WAVEFORM(waveform));
-    patch_set_mark_frame(patch, mark, val);
-    update_mark_val(val);
-    update_fade_spins(); /* so xfade spin button range is set correctly */
-    gtk_widget_queue_draw(waveform);
-    gtk_widget_queue_draw(wf_thumb);
-}
-
-
-static void cb_fade_spin_changed(GtkWidget * spin, gpointer data)
-{
-    debug("fade changed\n");
-    int val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(fade_spin));
-    patch_set_fade_samples(patch, val);
-}
-
-
-static void cb_xfade_spin_changed(GtkWidget * spin, gpointer data)
-{
-    debug("x-fade changed\n");
-    int val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(xfade_spin));
-    patch_set_xfade_samples(patch, val);
-    update_mark_spin(); /* so mark spin range is set correctly */
-}
-
-
-static void update_mark_spin(void)
-{
-    int min;
-    int max;
-
-    int mark = waveform_get_mark(WAVEFORM(waveform));
-    int val =  patch_get_mark_frame_range(patch, mark, &min, &max);
-
-    debug("updating mark spin\n");
-
-    if (val < 0)
-    {
-        val = patch_get_mark_frame(patch, mark);
-        gtk_widget_set_sensitive(mark_spin, FALSE);
-    }
-    else
-        gtk_widget_set_sensitive(mark_spin, TRUE);
-
-    g_signal_handlers_block_by_func(mark_spin, cb_mark_spin_changed, 0);
-    gtk_spin_button_set_range(GTK_SPIN_BUTTON(mark_spin), min, max);
-    gtk_spin_button_set_value(GTK_SPIN_BUTTON(mark_spin), val);
-    g_signal_handlers_unblock_by_func(mark_spin, cb_mark_spin_changed, 0);
-
-    update_fade_spins(); /* ie set the range of xfade */
-    update_mark_val(val);
-}
-
-
-static void update_fade_spins(void)
-{
-    int fade = patch_get_fade_samples(patch);
-    int max_fade = patch_get_max_fade_samples(patch);
-    int xfade = patch_get_xfade_samples(patch);
-    int max_xfade = patch_get_max_xfade_samples(patch);
-
-    g_signal_handlers_block_by_func(fade_spin, cb_fade_spin_changed, 0);
-    gtk_spin_button_set_range(GTK_SPIN_BUTTON(fade_spin), 0, max_fade);
-    gtk_spin_button_set_value(GTK_SPIN_BUTTON(fade_spin), fade);
-    g_signal_handlers_unblock_by_func(fade_spin, cb_fade_spin_changed, 0);
-
-    g_signal_handlers_block_by_func(xfade_spin, cb_xfade_spin_changed, 0);
-    gtk_spin_button_set_range(GTK_SPIN_BUTTON(xfade_spin), 0, max_xfade);
-    gtk_spin_button_set_value(GTK_SPIN_BUTTON(xfade_spin), xfade);
-    g_signal_handlers_unblock_by_func(xfade_spin, cb_xfade_spin_changed, 0);
-}
-
-
-static void cb_mark_combo_changed(GtkWidget* combo, gpointer data)
-{
-    debug("combo changing...\n");
-    ignore_mark_change = TRUE;
-    waveform_set_mark(WAVEFORM(waveform),
-                    gtk_combo_box_get_active(GTK_COMBO_BOX(mark_combo)));
-    update_mark_spin();
-}
-
-static void cb_wf_play_changed(void)
-{
-    /*  there's some chance the play point that was changed is
-        selected and shown in the mark spin. */
-    debug("play changing...\n");
-    update_mark_spin();
-    update_fade_spins();
-    gtk_widget_queue_draw(wf_thumb);
-}
-
-static void cb_wf_loop_changed(void)
-{
-    /*  there's some chance the play point that was changed is
-        selected and shown in the mark spin. */
-    debug("loop changing...\n");
-    update_mark_spin();
-    update_fade_spins();
-    gtk_widget_queue_draw(wf_thumb);
-}
-
-static void cb_wf_mark_changed(void)
-{
-    /* ditto cb_wf_play_changed */
-    if (ignore_mark_change)
-    {
-        debug("ignoring mark change this time\n");
-        ignore_mark_change = FALSE;
-        return;
-    }
-    debug("mark changing...\n");
-    gtk_combo_box_set_active(GTK_COMBO_BOX(mark_combo),
-                                waveform_get_mark(WAVEFORM(waveform)));
-}
-
-static void cb_wf_view_changed(void)
-{
-    float start, stop;
-    waveform_get_range(WAVEFORM(waveform), &start, &stop);
-    debug("view changing...\n");
-    g_signal_handlers_block_by_func(hscroll, cb_scroll, 0);
-    gtk_adjustment_set_value(GTK_ADJUSTMENT(hscrolladj), start);
-    g_signal_handlers_unblock_by_func(hscroll, cb_scroll, 0);
-}
-
-static void cb_zoom(GtkAdjustment* adj, GtkWidget* spinbutton)
-{
-    (void)spinbutton;
-    float max;
-    float page_size;
-    float step_inc;
-    float page_inc;
-    float val;
-
-    float start;
-    float stop;
-    int frames = patch_get_frames(patch);
-
-    waveform_get_range(WAVEFORM(waveform), &start, &stop);
-
-    debug("zoom changing...\n");
-
-    zoom = gtk_adjustment_get_value(adj);
-
-    if (zoom < ZOOM_MIN)
-        zoom = ZOOM_MIN;
-    else if (zoom > ZOOM_MAX)
-        zoom = ZOOM_MAX;
-
-    max = 1.0 - (1.0 / zoom);
-    page_size = 1.0 / zoom;
-    page_inc = max / 1000;
-    step_inc = max / 10;
-
-    /* create new adjustment */
-
-    val = start + (stop - start) / 2 - page_size / 2;
-
-    /* range needs to be updated so that we can tell the waveform
-     * object how much of itself it needs to draw when the scrollbar
-     * changes */
-
-    range = 1.0 - max;
-
-    if (val + range > 1.0)
-        val = 1.0 - range;
-
-
-debug("start:%f stop:%f hscroll range value:%f\n", start,stop,val);
-
-    gtk_range_set_range(GTK_RANGE(hscroll), start, stop);
-
-    hscrolladj = gtk_adjustment_new(val, 0.0, 1.0,  step_inc,
-                                                    page_inc,
-                                                    page_size);
-    gtk_range_set_adjustment(GTK_RANGE(hscroll),
-                                            GTK_ADJUSTMENT(hscrolladj));
-
-
-     /* emit value-changed signal so waveform is redrawn with
-      * the new dimensions */
-    g_signal_emit_by_name(G_OBJECT(hscroll), "value-changed");
-}
-
-
-
-void sample_editor_show(int id)
-{
-    waveform_set_patch (WAVEFORM (waveform), id);
-
-    patch = id;
-
-    old_play_start = patch_get_mark_frame(patch, WF_MARK_PLAY_START);
-    old_play_stop =  patch_get_mark_frame(patch, WF_MARK_PLAY_STOP);
-    old_loop_start = patch_get_mark_frame(patch, WF_MARK_LOOP_START);
-    old_loop_stop =  patch_get_mark_frame(patch, WF_MARK_LOOP_STOP);
-
-    gtk_combo_box_set_active(GTK_COMBO_BOX(mark_combo), WF_MARK_PLAY_START);
-
-    update_fade_spins();
-
-    gtk_widget_show (window);
-}
-
-
-void sample_editor_update(void)
-{
-    gtk_widget_queue_draw(waveform);
-}
-
-
-
-void sample_editor_set_thumb(GtkWidget* thumb)
-{
-    wf_thumb = thumb;
-}
-
-
-void sample_editor_init(GtkWidget * parent)
-{
-     GtkWindow *w;
-     GtkWidget *master_vbox;
-     GtkWidget *hbox;
-     GtkWidget *button;
-     GtkWidget *image;
-     GtkWidget *label;
-     GtkWidget *spinbutton;
-     GtkAdjustment *zoom_adj;
-     GtkWidget* tmp;
-
-     debug ("Initializing sample editor window\n");
-
-     /* main window */
-     window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-     w = GTK_WINDOW (window);
-     gtk_window_set_title (w, "Edit Sample");
-     gtk_window_set_resizable (w, TRUE);
-     gtk_window_set_transient_for (w, GTK_WINDOW (parent));
-     gtk_window_set_modal (w, FALSE);
-     g_signal_connect (G_OBJECT(w), "delete-event", G_CALLBACK(cb_close),
-                                                                    NULL);
-
-     /* master vbox */
-     master_vbox = gtk_vbox_new (FALSE, GUI_SPACING);
-     gtk_container_add (GTK_CONTAINER (window), master_vbox);
-     gtk_container_set_border_width (GTK_CONTAINER (window), GUI_SPACING);
-     gtk_widget_show (master_vbox);
-
-
-     /* top row hbox */
-     hbox = gtk_hbox_new (FALSE, GUI_SPACING);
-     gtk_box_pack_start (GTK_BOX (master_vbox), hbox, FALSE, FALSE, 0);
-     gtk_widget_show (hbox);
-
-    /* play button */
-    image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY,
-                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
-    button = gtk_button_new();
-    gtk_container_add(GTK_CONTAINER(button), image);
-    gtk_box_pack_start(GTK_BOX (hbox), button, FALSE, FALSE, 0);
-    g_signal_connect_swapped(G_OBJECT (button), "clicked",
-                                    G_CALLBACK(cb_play), NULL);
-    gtk_widget_show(image);
-    gtk_widget_show(button);
-
-    /* stop button */
-    image = gtk_image_new_from_stock(GTK_STOCK_MEDIA_STOP,
-                                     GTK_ICON_SIZE_SMALL_TOOLBAR);
-    button = gtk_button_new ( );
-    gtk_container_add (GTK_CONTAINER (button), image);
-    gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
-    g_signal_connect_swapped (G_OBJECT (button), "clicked",
-                                    G_CALLBACK (cb_stop), NULL);
-    gtk_widget_show(image);
-    gtk_widget_show(button);
-
-    /* separator */
-    tmp = gtk_vseparator_new();
-    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
-    gtk_widget_show(tmp);
-
-    /* fade spin button */
-    label = gtk_label_new("Fade:");
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-    gtk_widget_show(label);
-
-    fade_spin = gtk_spin_button_new_with_range(0, 1, 1);
-    gtk_box_pack_start(GTK_BOX(hbox), fade_spin, FALSE, FALSE, 0);
-    gtk_widget_show(fade_spin);
-    g_signal_connect(G_OBJECT(fade_spin), "value-changed",
-                            G_CALLBACK(cb_fade_spin_changed), NULL);
-
-    /* X-fade spin button */
-    label = gtk_label_new("X-Fade:");
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-    gtk_widget_show(label);
-
-    xfade_spin = gtk_spin_button_new_with_range(0, 1, 1);
-    gtk_box_pack_start(GTK_BOX(hbox), xfade_spin, FALSE, FALSE, 0);
-    gtk_widget_show(xfade_spin);
-    g_signal_connect(G_OBJECT(xfade_spin), "value-changed",
-                            G_CALLBACK(cb_xfade_spin_changed), NULL);
-
-    /* separator */
-    tmp = gtk_vseparator_new();
-    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
-    gtk_widget_show(tmp);
-
-
-    /* mark combo */
-    mark_combo = basic_combo_create(waveform_get_mark_names());
-    gtk_box_pack_start(GTK_BOX (hbox), mark_combo, FALSE, FALSE, 0);
-    gtk_widget_show(mark_combo);
-    g_signal_connect(G_OBJECT(mark_combo), "changed",
-                            G_CALLBACK(cb_mark_combo_changed), NULL);
-
-    mark_spin = gtk_spin_button_new_with_range(0, 1, 1);
-    gtk_box_pack_start(GTK_BOX (hbox), mark_spin, FALSE, FALSE, 0);
-    gtk_widget_show(mark_spin);
-    g_signal_connect(G_OBJECT(mark_spin), "value-changed",
-                            G_CALLBACK(cb_mark_spin_changed), NULL);
-
-    /* mark spin value label */
-    mark_val = gtk_label_new("");
-    gtk_box_pack_start(GTK_BOX (hbox), mark_val, FALSE, FALSE, 0);
-    gtk_widget_show(mark_val);
-
-    /* separator */
-    tmp = gtk_vseparator_new();
-    gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 0);
-    gtk_widget_show(tmp);
-
-     /* loop points clear button */
-     button = gtk_button_new_with_label ("Loop");
-     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
-     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_clear),
-		       (gpointer) "loop");
-     gtk_widget_show (button);
-
-     /* play points clear button */
-     button = gtk_button_new_with_label ("Play");
-     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
-     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_clear),
-                                                        (gpointer) "play");
-     gtk_widget_show (button);
-
-
-     /* clear label */
-     label = gtk_label_new ("Clear Points:");
-     gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-     gtk_widget_show (label);
-
-     /* waveform */
-     waveform = waveform_new();
-     waveform_set_patch(        WAVEFORM(waveform), -1);
-     waveform_set_size(         WAVEFORM(waveform),  512, 256);
-     waveform_set_interactive(  WAVEFORM(waveform), TRUE);
-     gtk_box_pack_start (GTK_BOX (master_vbox), waveform, TRUE, TRUE, 0);
-     gtk_widget_show (waveform);
-     g_signal_connect(G_OBJECT (waveform), "play-changed",
-                            G_CALLBACK(cb_wf_play_changed), NULL);
-     g_signal_connect(G_OBJECT (waveform), "loop-changed",
-                            G_CALLBACK(cb_wf_loop_changed), NULL);
-     g_signal_connect(G_OBJECT (waveform), "mark-changed",
-                            G_CALLBACK(cb_wf_mark_changed), NULL);
-     g_signal_connect(G_OBJECT (waveform), "view-changed",
-                            G_CALLBACK(cb_wf_view_changed), NULL);
-
-     /* waveform scrollbar */
-     hscrolladj = gtk_adjustment_new (0.0, 0.0, 1.0, 0.0, 0.0, 1.0);
-     hscroll = gtk_hscrollbar_new (GTK_ADJUSTMENT (hscrolladj));
-     gtk_box_pack_start (GTK_BOX (master_vbox), hscroll, FALSE, FALSE, 0);
-     g_signal_connect (G_OBJECT (hscroll), "value-changed",
-                                            G_CALLBACK (cb_scroll), NULL);
-     gtk_widget_show (hscroll);
-
-     /* hbox */
-     hbox = gtk_hbox_new (FALSE, GUI_SPACING);
-     gtk_box_pack_start (GTK_BOX (master_vbox), hbox, FALSE, FALSE, 0);
-     gtk_widget_show (hbox);
-
-     /* zoom spinbutton */
-     label = gtk_label_new ("Zoom:");
-     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-     gtk_widget_show (label);
-
-     zoom_adj =
-	  (GtkAdjustment *) gtk_adjustment_new (1.0, ZOOM_MIN, ZOOM_MAX, 1,
-						5, 0.0);
-     spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (zoom_adj), 1, 2);
-     gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
-     g_signal_connect (G_OBJECT (zoom_adj), "value_changed",
-		       G_CALLBACK (cb_zoom), (gpointer) spinbutton);
-     gtk_widget_show (spinbutton);
-
-
-     /* close button */
-     button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
-     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
-     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_close),
-		       NULL);
-     gtk_widget_show (button);
-
-     /* reset button */
-     button = gtk_button_new_with_label ("Reset");
-     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
-     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_reset),
-		       NULL);
-     gtk_widget_show (button);
-}
diff --git a/src/gui/voicetab.c b/src/gui/voicetab.c
deleted file mode 100644
index d6b49fe..0000000
--- a/src/gui/voicetab.c
+++ /dev/null
@@ -1,333 +0,0 @@
-/*  Petri-Foo is a fork of the Specimen audio sampler.
-
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
-    Copyright 2011 James W. Morris
-
-    This file is part of Petri-Foo.
-
-    Petri-Foo is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2 as
-    published by the Free Software Foundation.
-
-    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
-*/
-
-
-#include <gtk/gtk.h>
-#include <phat/phat.h>
-#include "voicetab.h"
-#include "gui.h"
-#include "patch_set_and_get.h"
-
-
-typedef struct _VoiceTabPrivate VoiceTabPrivate;
-
-#define VOICE_TAB_GET_PRIVATE(obj)      \
-    (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
-        VOICE_TAB_TYPE, VoiceTabPrivate))
-
-struct _VoiceTabPrivate
-{
-    int patch;
-    guint refresh;
-    GtkWidget* cut_sb;
-    GtkWidget* cutby_sb;
-    GtkWidget* time_fan;
-    GtkWidget* mono_check;
-    GtkWidget* legato_check;
-    GtkWidget* porta_check;
-};
-
-
-G_DEFINE_TYPE(VoiceTab, voice_tab, GTK_TYPE_VBOX);
-
-
-static void voice_tab_class_init(VoiceTabClass* klass)
-{
-    GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
-    voice_tab_parent_class = g_type_class_peek_parent(klass);
-    g_type_class_add_private(object_class, sizeof(VoiceTabPrivate));
-}
-
-
-static void cut_cb(PhatSliderButton* button, VoiceTabPrivate* p)
-{
-    int val = phat_slider_button_get_value(button);
-    patch_set_cut(p->patch, val);
-}
-
-
-static void cutby_cb(PhatSliderButton* button, VoiceTabPrivate* p)
-{
-    int val = phat_slider_button_get_value(button);
-    patch_set_cut_by(p->patch, val);
-}
-
-
-static void porta_cb(GtkToggleButton* button, VoiceTabPrivate* p)
-{
-    patch_set_portamento(p->patch, gtk_toggle_button_get_active(button));
-}
-
-
-static void porta_cb2(GtkToggleButton* button, VoiceTabPrivate* p)
-{
-    if (gtk_toggle_button_get_active(button))
-        gtk_widget_set_sensitive(p->time_fan, TRUE);
-    else
-        gtk_widget_set_sensitive(p->time_fan, FALSE);
-}
-
-
-static void time_cb(PhatFanSlider* fan, VoiceTabPrivate* p)
-{
-    float val = phat_fan_slider_get_value(fan);
-    patch_set_portamento_time(p->patch, val);
-}
-
-
-static void mono_cb(GtkToggleButton* button, VoiceTabPrivate* p)
-{
-    patch_set_monophonic(p->patch, gtk_toggle_button_get_active(button));
-}
-
-
-static void mono_cb2(GtkToggleButton* button, VoiceTabPrivate* p)
-{
-    gtk_widget_set_sensitive(p->legato_check, 
-                            gtk_toggle_button_get_active(button));
-}
-
-
-static void legato_cb(GtkToggleButton* button, VoiceTabPrivate* p)
-{
-    patch_set_legato(p->patch, gtk_toggle_button_get_active(button));
-}
-
-
-static void connect(VoiceTabPrivate* p)
-{
-    g_signal_connect(G_OBJECT(p->cut_sb), "value-changed",
-                        G_CALLBACK(cut_cb), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->cutby_sb), "value-changed",
-                        G_CALLBACK(cutby_cb), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->mono_check), "toggled",
-                        G_CALLBACK(mono_cb), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->mono_check), "toggled",
-                        G_CALLBACK(mono_cb2), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->legato_check), "toggled",
-                        G_CALLBACK(legato_cb), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->porta_check), "toggled",
-                        G_CALLBACK(porta_cb), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->porta_check), "toggled",
-                        G_CALLBACK(porta_cb2), (gpointer)p);
-    g_signal_connect(G_OBJECT(p->time_fan), "value-changed",
-                        G_CALLBACK(time_cb), (gpointer)p);
-}
-
-
-static void block(VoiceTabPrivate* p)
-{
-    g_signal_handlers_block_by_func(p->cut_sb,      cut_cb,     p);
-    g_signal_handlers_block_by_func(p->cutby_sb,    cutby_cb,   p);
-    g_signal_handlers_block_by_func(p->mono_check,  mono_cb,    p);
-    g_signal_handlers_block_by_func(p->legato_check,legato_cb,  p);
-    g_signal_handlers_block_by_func(p->porta_check, porta_cb,   p);
-    g_signal_handlers_block_by_func(p->time_fan,    time_cb,    p);
-    /* *_cb2 intentionally omitted */
-}
-
-
-static void unblock(VoiceTabPrivate* p)
-{
-    g_signal_handlers_unblock_by_func(p->cut_sb,        cut_cb,     p);
-    g_signal_handlers_unblock_by_func(p->cutby_sb,      cutby_cb,   p);
-    g_signal_handlers_unblock_by_func(p->mono_check,    mono_cb,    p);
-    g_signal_handlers_unblock_by_func(p->legato_check,  legato_cb,  p);
-    g_signal_handlers_unblock_by_func(p->porta_check,   porta_cb,   p);
-    g_signal_handlers_unblock_by_func(p->time_fan,      time_cb,    p);
-    /* *_cb2 intentionally omitted */
-}
-
-
-static gboolean refresh(gpointer data)
-{
-    VoiceTabPrivate* p = data;
-    gboolean porta;
-    float time;
-
-    if (p->patch < 0)
-        return TRUE;
-
-    porta = patch_get_portamento(p->patch);
-    time = patch_get_portamento_time(p->patch);
-
-    block(p);
-
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->porta_check), porta);
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->time_fan), time);
-
-    unblock(p);
-
-    return TRUE;
-}
-
-
-static void voice_tab_init(VoiceTab* self)
-{
-    VoiceTabPrivate* p = VOICE_TAB_GET_PRIVATE(self);
-    GtkBox* box = GTK_BOX(self);
-    GtkWidget* title;
-    GtkWidget* table;
-    GtkTable* t;
-    GtkWidget* pad;
-    GtkWidget* label;
-
-    p->patch = -1;
-    p->refresh = -1;
-
-    gtk_container_set_border_width(GTK_CONTAINER(self), GUI_BORDERSPACE);
-
-    /* table */
-    table = gtk_table_new(8, 6, FALSE);
-    t = (GtkTable*) table;
-    gtk_box_pack_start(box, table, FALSE, FALSE, 0);
-    gtk_widget_show(table);
-    
-    /* voice title */
-    title = gui_title_new("Voice");
-    gtk_table_attach_defaults(t, title, 0, 6, 0, 1);
-    gtk_widget_show(title);
-
-    /* indentation */
-    pad = gui_hpad_new(GUI_INDENT);
-    gtk_table_attach(t, pad, 0, 1, 1, 2, 0, 0, 0, 0);
-    gtk_widget_show(pad);
-    
-    /* voice title padding */
-    pad = gui_vpad_new(GUI_TITLESPACE);
-    gtk_table_attach(t, pad, 1, 2, 1, 2, 0, 0, 0, 0);
-    gtk_widget_show(pad);
-
-    /* portamento title padding */
-    pad = gui_vpad_new(GUI_TITLESPACE);
-    gtk_table_attach(t, pad, 1, 2, 6, 7, 0, 0, 0, 0);
-    gtk_widget_show(pad);
-
-    /* cut-mono column spacing */
-    pad = gui_hpad_new(GUI_SECSPACE);
-    gtk_table_attach(t, pad, 4, 5, 1, 2, 0, 0, 0, 0);
-    gtk_widget_show(pad);
-
-    /* time-fan column spacing */
-    pad = gui_hpad_new(GUI_TEXTSPACE);
-    gtk_table_attach(t, pad, 2, 3, 1, 2, 0, 0, 0, 0);
-    gtk_widget_show(pad);
-
-    /* section spacing */
-    pad = gui_vpad_new(GUI_SECSPACE);
-    gtk_table_attach(t, pad, 0, 1, 4, 5, 0, 0, 0, 0);
-    gtk_widget_show(pad);
-
-    /* cut sliderbutton */
-    p->cut_sb = phat_slider_button_new_with_range(0, 0, 99, 1, 0);
-    phat_slider_button_set_format(PHAT_SLIDER_BUTTON(p->cut_sb), 0,
-                                                    "Cut:", NULL);
-    phat_slider_button_set_threshold(PHAT_SLIDER_BUTTON(p->cut_sb),
-                                                    GUI_THRESHOLD);
-    gtk_table_attach_defaults(t, p->cut_sb, 1, 4, 2, 3);
-    gtk_widget_show(p->cut_sb);
-
-    gtk_table_set_row_spacing(t, 2, GUI_SPACING);
-
-    /* cutby sliderbutton */
-    p->cutby_sb = phat_slider_button_new_with_range(0, 0, 99, 1, 0);
-    phat_slider_button_set_format(PHAT_SLIDER_BUTTON(p->cutby_sb), 0,
-                                                    "Cut by:", NULL);
-    phat_slider_button_set_threshold(PHAT_SLIDER_BUTTON(p->cutby_sb),
-                                                    GUI_THRESHOLD);
-    gtk_table_attach_defaults(t, p->cutby_sb, 1, 4, 3, 4);
-    gtk_widget_show(p->cutby_sb);
-
-    /* mono sliderbutton */
-    p->mono_check = gtk_check_button_new_with_label("Monophonic");
-    gtk_table_attach_defaults(t, p->mono_check, 5, 6, 2, 3);
-    gtk_widget_show(p->mono_check);
-
-    /* legato sliderbutton */
-    p->legato_check = gtk_check_button_new_with_label("Legato");
-    gtk_table_attach_defaults(t, p->legato_check, 5, 6, 3, 4);
-    gtk_widget_show(p->legato_check);
-    gtk_widget_set_sensitive(p->legato_check, FALSE);
-
-    /* portamento checkbox */
-    title = gui_title_new("Portamento");
-    p->porta_check = gtk_check_button_new();
-    gtk_container_add(GTK_CONTAINER(p->porta_check), title);
-    gtk_table_attach_defaults(t, p->porta_check, 0, 6, 5, 6);
-    gtk_widget_show(title);
-    gtk_widget_show(p->porta_check);
-
-    /* time fan */
-    label = gtk_label_new("Time:");
-    p->time_fan = phat_hfan_slider_new_with_range(0.05, 0.0, 1.0, 0.01);
-    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-    gtk_table_attach(t, label, 1, 2, 7, 8, GTK_FILL, 0, 0, 0);
-    gtk_table_attach_defaults(t, p->time_fan, 3, 4, 7, 8);
-    gtk_widget_show(label);
-    gtk_widget_show(p->time_fan);
-    gtk_widget_set_sensitive(p->time_fan, FALSE);
-
-    /* done */
-    connect(p);
-    p->refresh = g_timeout_add(GUI_REFRESH_TIMEOUT, refresh, (gpointer)p);
-}
-
-
-GtkWidget* voice_tab_new(void)
-{
-    return (GtkWidget*) g_object_new(VOICE_TAB_TYPE, NULL);
-}
-
-
-void voice_tab_set_patch(VoiceTab* self, int patch)
-{
-    VoiceTabPrivate* p = VOICE_TAB_GET_PRIVATE(self);
-    int cut, cutby;
-    gboolean porta, mono, legato;
-    float time;
-
-    p->patch = patch;
-
-    if (patch < 0)
-        return;
-
-    cut = patch_get_cut(patch);
-    cutby = patch_get_cut_by(patch);
-    porta = patch_get_portamento(patch);
-    mono = patch_get_monophonic(patch);
-    legato = patch_get_legato(patch);
-    time = patch_get_portamento_time(patch);
-
-    block(p);
-
-    phat_slider_button_set_value(PHAT_SLIDER_BUTTON(p->cut_sb), cut);
-    phat_slider_button_set_value(PHAT_SLIDER_BUTTON(p->cutby_sb), cutby);
-
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->porta_check), porta);
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->mono_check), mono);
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p->legato_check),legato);
-
-    phat_fan_slider_set_value(PHAT_FAN_SLIDER(p->time_fan), time);
-
-    unblock(p);
-}
diff --git a/src/patch_private/Makefile.am b/src/patch_private/Makefile.am
deleted file mode 100644
index 6eb708a..0000000
--- a/src/patch_private/Makefile.am
+++ /dev/null
@@ -1,18 +0,0 @@
-
-noinst_LIBRARIES = libpatch.a
-
-libpatch_a_SOURCES = 					\
-	patch_data.c		patch_data.h		\
-	patch_defs.c		patch_defs.h		\
-	patch_macros.h						\
-	patch_voice.c		patch_voice.h
-
-
-
-
-INCLUDES = 			\
-	-l..
-
-
-libpatch_a_CFLAGS = $(CFLAGS) -I.. -DINSTALLDIR=\"$(datadir)\"
-
diff --git a/src/patch_set_and_get.c b/src/patch_set_and_get.c
deleted file mode 100644
index 7e24473..0000000
--- a/src/patch_set_and_get.c
+++ /dev/null
@@ -1,1891 +0,0 @@
-/*  Petri-Foo is a fork of the Specimen audio sampler.
-
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
-    Copyright 2011 James W. Morris
-
-    This file is part of Petri-Foo.
-
-    Petri-Foo is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2 as
-    published by the Free Software Foundation.
-
-    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
-*/
-
-
-#include "patch_set_and_get.h"
-
-
-#include <string.h>
-#include <math.h>
-
-
-#include "sample.h"
-#include "adsr.h"
-#include "lfo.h"
-
-
-#include "patch_private/patch_data.h"
-#include "patch_private/patch_defs.h"
-#include "patch_private/patch_macros.h"
-
-
-INLINE_ISOK_DEF
-
-
-inline static int mod_src_to_eg_index(int patch_id, int id)
-{
-    if (!isok(patch_id))
-        return PATCH_ID_INVALID;
-
-    id -= MOD_SRC_EG;
-
-    return (id >= 0 && id < VOICE_MAX_ENVS)
-                ? id
-                : PATCH_ENV_ID_INVALID;
-}
-
-
-static int mod_src_ok(int id)
-{
-    return (id & MOD_SRC_ALL || id == MOD_SRC_NONE)
-            ? 0 
-            : PATCH_MOD_SRC_INVALID;
-}
-
-
-inline static bool mark_ok(int id)
-{
-    if (id < WF_MARK_START || id > WF_MARK_STOP)
-        return false;
-
-    return true;
-}
-
-inline static bool mark_settable(int id)
-{
-    if (id < WF_MARK_PLAY_START || id > WF_MARK_PLAY_STOP)
-        return false;
-
-    return true;
-}
-
-
-static inline void set_mark_frame(int patch, int mark, int frame)
-{
-    *(patches[patch]->marks[mark]) = frame;
-}
-
-
-static inline int get_mark_frame(int patch, int mark)
-{
-    return *(patches[patch]->marks[mark]);
-}
-
-
-static int get_mark_frame_range(int patch, int mark, int* min, int* max)
-{
-    int xfade = patches[patch]->xfade_samples;
-
-    if (mark == WF_MARK_START || mark == WF_MARK_STOP)
-    {
-        *min = *max = get_mark_frame(patch, mark);
-        /* indicate non-editable! */
-        return -1;
-    }
-
-    /* potential range: */
-    *min = get_mark_frame(patch, mark - 1);
-    *max = get_mark_frame(patch, mark + 1);
-
-    /* tweak if necessary */
-    switch(mark)
-    {
-    case WF_MARK_PLAY_START:
-        *max -= xfade;
-        break;
-    case WF_MARK_PLAY_STOP:
-        *min += xfade;
-        break;
-    case WF_MARK_LOOP_START:
-        *min += xfade;
-        *max -= xfade;
-        break;
-    case WF_MARK_LOOP_STOP:
-        *min += xfade;
-        *max -= xfade;
-        break;
-    }
-
-    return get_mark_frame(patch, mark);
-}
-
-
-static int get_patch_param(int patch_id, PatchParamType param,
-                                                            PatchParam** p)
-{
-    if (!isok(patch_id))
-        return PATCH_ID_INVALID;
-
-    switch(param)
-    {
-    case PATCH_PARAM_AMPLITUDE: *p = &patches[patch_id]->vol;      break;
-    case PATCH_PARAM_PANNING:   *p = &patches[patch_id]->pan;      break;
-    case PATCH_PARAM_CUTOFF:    *p = &patches[patch_id]->ffreq;    break;
-    case PATCH_PARAM_RESONANCE: *p = &patches[patch_id]->freso;    break;
-    case PATCH_PARAM_PITCH:     *p = &patches[patch_id]->pitch;    break;
-    default:
-        debug ("Invalid request for address of param\n");
-        return PATCH_PARAM_INVALID;
-    }
-
-    return 0;
-}
-
-
-/* inline static function def macro, see private/patch_data.h */
-INLINE_PATCH_TRIGGER_GLOBAL_LFO_DEF
-
-
-
-/**************************************************************************/
-/************************* ENVELOPE SETTERS *******************************/
-/**************************************************************************/
-
-
-int patch_set_env_on (int patch_id, int eg, bool state)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    patches[patch_id]->env_params[eg].env_on = state;
-    return 0;
-}
-
-
-/* sets the delay length in seconds */
-int patch_set_env_delay (int patch_id, int eg, float secs)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].delay = secs;
-    return 0;
-}
-
-/* sets the attack length in seconds */
-int patch_set_env_attack (int patch_id, int eg, float secs)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].attack = secs;
-    return 0;
-}
-
-/* sets the hold length in seconds */
-int patch_set_env_hold (int patch_id, int eg, float secs)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].hold = secs;
-    return 0;
-}
-
-/* sets the decay length in seconds */
-int patch_set_env_decay (int patch_id, int eg, float secs)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].decay = secs;
-    return 0;
-}
-
-/* sets the sustain level */
-int patch_set_env_sustain (int patch_id, int eg, float level)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (level < 0.0 || level > 1.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].sustain = level;
-    return 0;
-}
-
-/* sets the release length in seconds */
-int patch_set_env_release (int patch_id, int eg, float secs)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    if (secs < PATCH_MIN_RELEASE)
-        secs = PATCH_MIN_RELEASE;
-
-    patches[patch_id]->env_params[eg].release = secs;
-    return 0;
-}
-
-/* sets key tracking amount */
-int patch_set_env_key_amt(int patch_id, int eg, float val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    if (val < -1.0 || val > 1.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].key_amt = val;
-    return 0;
-}
-
-/*
-int patch_set_env_vel_amt(int patch_id, int eg, float val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg;
-
-    if (val < -1.0 || val > 1.0)
-        return PATCH_PARAM_INVALID;
-
-    patches[patch_id]->env_params[eg].vel_amt = val;
-    return 0;
-}
-*/
-
-/**************************************************************************/
-/************************* ENVELOPE GETTERS *******************************/
-/**************************************************************************/
-
-/* places the delay length in seconds into val */
-int patch_get_env_on(int patch_id, int eg, bool* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].env_on;
-    return 0;
-}
-
-
-
-/* places the delay length in seconds into val */
-int patch_get_env_delay (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].delay;
-    return 0;
-}
-
-
-/* places the attack length in seconds into val */
-int patch_get_env_attack (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].attack;
-    return 0;
-}
-
-
-/* places the hold length in seconds into val */
-int patch_get_env_hold (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].hold;
-    return 0;
-}
-
-
-/* places the decay length in seconds into val */
-int patch_get_env_decay (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].decay;
-    return 0;
-}
-
-
-/* places the sustain level into val */
-int patch_get_env_sustain (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].sustain;
-    return 0;
-}
-
-/* places the release length in seconds into val */
-int patch_get_env_release (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].release;
-
-    /* we hide the fact that we have a min release value from the
-     * outside world (they're on a need-to-know basis) */
-    if (*val <= PATCH_MIN_RELEASE)
-        *val = 0;
-    return 0;
-}
-
-
-int patch_get_env_key_amt (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg; /* as error code */
-
-    *val = patches[patch_id]->env_params[eg].key_amt;
-    return 0;
-}
-
-/*
-int patch_get_env_vel_amt (int patch_id, int eg, float* val)
-{
-    eg = mod_src_to_eg_index(patch_id, eg);
-
-    if (eg < 0)
-        return eg;
-
-    *val = patches[patch_id]->env_params[eg].vel_amt;
-    return 0;
-}
-*/
-/************************************************************************/
-/*************************** LFO SETTERS ********************************/
-/************************************************************************/
-
-static int lfo_from_id(int patch_id, int id, LFO** lfo, LFOParams** lfopar)
-{
-    if (lfo)
-        *lfo = 0;
-
-    *lfopar = 0;
-
-    if (!isok(patch_id))
-        return PATCH_ID_INVALID;
-
-    if ((id & MOD_SRC_VLFO) && (id & MOD_SRC_GLFO))
-        return PATCH_LFO_ID_INVALID;
-
-    if (id & MOD_SRC_VLFO)
-    {
-        id -= MOD_SRC_VLFO;
-
-        if (id < VOICE_MAX_LFOS)
-            *lfopar = &patches[patch_id]->vlfo_params[id];
-
-        return 0;
-    }
-
-    if (id & MOD_SRC_GLFO)
-    {
-        id -= MOD_SRC_GLFO;
-
-        if (id < PATCH_MAX_LFOS)
-        {
-            if (lfo)
-                *lfo = patches[patch_id]->glfo[id];
-
-            *lfopar = &patches[patch_id]->glfo_params[id];
-        }
-
-        return 0;
-    }
-
-    return PATCH_LFO_ID_INVALID;
-}
-
-int patch_set_lfo_on (int patch_id, int lfo_id, bool state)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    lfopar->lfo_on = state;
-
-    if (lfo)
-        lfo_trigger(lfo, lfopar);
-
-    return 0;
-}
-
-/* set the attack time of the param's LFO */
-int patch_set_lfo_attack (int patch_id, int lfo_id, float secs)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    lfopar->attack = secs;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-/* set the period length of the param's lfo in beats */
-int patch_set_lfo_beats (int patch_id, int lfo_id, float beats)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    if (beats < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    lfopar->sync_beats = beats;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-/* set the delay time of the param's LFO */
-int patch_set_lfo_delay (int patch_id, int lfo_id, float secs)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    if (secs < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    lfopar->delay = secs;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-/* set the frequency of the param's lfo */
-int patch_set_lfo_freq (int patch_id, int lfo_id, float freq)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    if (freq < 0.0)
-        return PATCH_PARAM_INVALID;
-
-    lfopar->freq = freq;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-
-/* set whether to constrain the param's LFOs to positive values or not */
-int patch_set_lfo_positive (int patch_id, int lfo_id, bool state)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    lfopar->positive = state;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-
-/* set the param's lfo shape */
-int patch_set_lfo_shape (int patch_id, int lfo_id, LFOShape shape)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    lfopar->shape = shape;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-/* set whether to the param's lfo should sync to tempo or not */
-int patch_set_lfo_sync (int patch_id, int lfo_id, bool state)
-{
-    LFO*        lfo;
-    LFOParams*  lfopar;
-    int         err;
-
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-
-    lfopar->sync = state;
-
-    if (lfo)
-        lfo_rigger(lfo, lfopar);
-
-    return 0;
-}
-
-/************************************************************************/
-/*************************** LFO GETTERS ********************************/
-/************************************************************************/
-
-int patch_get_lfo_on(int patch_id, int lfo_id, bool* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->lfo_on;
-    return 0;
-}
-
-
-/* get the attack time of the param's LFO */
-int patch_get_lfo_attack (int patch_id, int lfo_id, float* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->attack;
-    return 0;
-}
-
-/* get the param's lfo period length in beats */
-int patch_get_lfo_beats (int patch_id, int lfo_id, float* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->sync_beats;
-    return 0;
-}
-
-/* get the delay time of the param's LFO */
-int patch_get_lfo_delay (int patch_id, int lfo_id, float* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->delay;
-    return 0;
-}
-
-/* get the param's lfo frequency */
-int patch_get_lfo_freq (int patch_id, int lfo_id, float* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->freq;
-    return 0;
-}
-
-int patch_get_lfo_positive (int patch_id, int lfo_id, bool* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->positive;
-    return 0;
-}
-
-/* get param's lfo shape */
-int patch_get_lfo_shape (int patch_id, int lfo_id, LFOShape* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->shape;
-    return 0;
-}
-
-/* get whether param's lfo is tempo synced or not */
-int patch_get_lfo_sync (int patch_id, int lfo_id, bool* val)
-{
-    LFOParams*  lfopar;
-    int         err;
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *val = lfopar->sync;
-    return 0;
-}
-
-
-/**************************************************************************/
-/************************ PARAMETER SETTERS *******************************/
-/**************************************************************************/
-
-/* sets channel patch listens on */
-int patch_set_channel (int id, int channel)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    if (channel < 0 || channel > 15)
-        return PATCH_CHANNEL_INVALID;
-
-    patches[id]->channel = channel;
-    return 0;
-}
-
-/* sets the cut signal this patch emits when activated */
-int patch_set_cut (int id, int cut)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-    patches[id]->cut = cut;
-    return 0;
-}
-
-/* sets the cut signal that terminates this patch if active */
-int patch_set_cut_by (int id, int cut_by)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-    patches[id]->cut_by = cut_by;
-    return 0;
-}
-
-/* sets filter cutoff frequency */
-int patch_set_cutoff (int id, float freq)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    if (freq < 0.0 || freq > 1.0)
-	return PATCH_PARAM_INVALID;
-
-    patches[id]->ffreq.val = freq;
-    return 0;
-}
-
-/* set whether this patch should be played legato or not */
-int patch_set_legato(int id, bool val)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-    patches[id]->legato = val;
-    return 0;
-}
-
-
-int patch_set_fade_samples(int id, int samples)
-{
-debug("set fade samples id:%d samples:%d\n",id,samples);
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    if (patches[id]->sample->sp == NULL)
-        return 0;
-
-    if (samples < 0)
-    {
-        debug ("refusing to set negative fade length\n");
-        return PATCH_PARAM_INVALID;
-    }
-
-    if (patches[id]->play_start + samples * 2 >= patches[id]->play_stop)
-    {
-        debug ("refusing to set fade length greater than half the "
-               " number of samples between play start and play stop\n");
-        return PATCH_PARAM_INVALID;
-    }
-
-    patches[id]->fade_samples = samples;
-    return 0;
-}
-
-int patch_set_xfade_samples(int id, int samples)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    if (patches[id]->sample->sp == NULL)
-        return 0;
-
-    if (samples < 0)
-    {
-        debug ("refusing to set negative xfade length\n");
-        return PATCH_PARAM_INVALID;
-    }
-
-    if (patches[id]->loop_start + samples > patches[id]->loop_stop)
-    {
-        debug ("refusing to set xfade length greater than samples"
-               " between play start and play stop\n");
-        return PATCH_PARAM_INVALID;
-    }
-
-    if (patches[id]->loop_stop + samples > patches[id]->play_stop)
-    {
-        debug ("refusing to set xfade length greater than samples"
-               " between loop stop and play stop\n");
-        return PATCH_PARAM_INVALID;
-    }
-
-debug("setting xfade to %d\n",samples);
-    patches[id]->xfade_samples = samples;
-    return 0;
-}
-
-
-/* sets the lower note of a patch's range */
-int patch_set_lower_note (int id, int note)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (note < 0 || note > 127)
-	return PATCH_NOTE_INVALID;
-
-    patches[id]->lower_note = note;
-    return 0;
-}
-
-
-int patch_set_mark_frame(int patch, int mark, int frame)
-{
-    if (!isok(patch))
-        return -1;
-
-    if (patches[patch]->sample->sp == NULL)
-        return -1;
-
-    if (!mark_settable(mark))
-        return -1;
-
-    int min;
-    int max;
-
-    get_mark_frame_range(patch, mark, &min, &max);
-
-    if (frame < min || frame > max)
-        return -1;
-
-    set_mark_frame(patch, mark, frame);
-    return mark;
-}
-
-
-int patch_set_mark_frame_expand(int patch, int mark, int frame,
-                                                     int* also_changed)
-{
-    int also = 0;
-    int also_frame = -1;
-    int xfade = patches[patch]->xfade_samples;
-    int fade = patches[patch]->fade_samples;
-
-    if (!isok(patch))
-        return -1;
-
-    if (patches[patch]->sample->sp == NULL)
-        return -1;
-
-    /*  if callee wishes not to be informed about which marks get changed
-        as a result of changing this one, also_changed will be NULL. It
-        needs to be a valid pointer.
-     */
-    if (!also_changed)
-        also_changed = &also;
-
-    *also_changed = -1;
-
-    switch(mark)
-    {
-    case WF_MARK_PLAY_START:
-        also_frame = frame + xfade; /* pot loop start pos */
-
-        if (frame + fade * 2 >= get_mark_frame(patch, WF_MARK_PLAY_STOP)
-         || also_frame >= get_mark_frame(patch, WF_MARK_LOOP_STOP))
-        {
-            mark = -1;
-        }
-        else if (also_frame > get_mark_frame(patch, WF_MARK_LOOP_START))
-        {   /* moving play start along pushes loop start along... */
-            if (also_frame + xfade
-                < get_mark_frame(patch, WF_MARK_LOOP_STOP))
-            {
-                *also_changed = WF_MARK_LOOP_START;
-            }
-            else
-                mark = -1;
-        }
-        break;
-
-    case WF_MARK_PLAY_STOP:
-        also_frame = frame - xfade; /* pot loop stop pos */
-
-        if (frame - fade * 2<= get_mark_frame(patch, WF_MARK_PLAY_START)
-         || also_frame <= get_mark_frame(patch, WF_MARK_LOOP_START))
-        {
-            mark = -1;
-        }
-        else if (also_frame < get_mark_frame(patch, WF_MARK_LOOP_STOP))
-        {
-            if (also_frame - xfade
-                > get_mark_frame(patch, WF_MARK_LOOP_START))
-            {
-                *also_changed = WF_MARK_LOOP_STOP;
-            }
-            else
-                mark = -1;
-        }
-        break;
-
-    case WF_MARK_LOOP_START:
-        also_frame = frame - xfade; /* pot play start pos */
-
-        if (frame + xfade >= get_mark_frame(patch, WF_MARK_LOOP_STOP))
-            mark = -1;
-        else if (also_frame < get_mark_frame(patch, WF_MARK_PLAY_START))
-        {
-            if (also_frame > 0)
-                *also_changed = WF_MARK_PLAY_START;
-            else
-                mark = -1;
-        }
-        break;
-
-    case WF_MARK_LOOP_STOP:
-        also_frame = frame + xfade /* pot play stop pos */;
-
-        if (frame - xfade <= get_mark_frame(patch, WF_MARK_LOOP_START))
-            mark = -1;
-        else if (also_frame > get_mark_frame(patch, WF_MARK_PLAY_STOP))
-        {
-            if (also_frame < get_mark_frame(patch, WF_MARK_STOP))
-                *also_changed = WF_MARK_PLAY_STOP;
-            else
-                mark = -1;
-        }
-        break;
-
-    default:
-        mark = -1;
-    }
-
-    if (mark != -1)
-    {
-        set_mark_frame(patch, mark, frame);
-    }
-
-    if (*also_changed != -1)
-    {
-        set_mark_frame(patch, *also_changed, also_frame);
-    }
-
-    return mark;
-}
-
-
-/* set whether the patch is monophonic or not */
-int patch_set_monophonic(int id, bool val)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    patches[id]->mono = val;
-    return 0;
-}
-
-/* sets the name */
-int patch_set_name (int id, const char *name)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-    strncpy (patches[id]->name, name, PATCH_MAX_NAME);
-    return 0;
-}
-
-/* sets the root note */
-int patch_set_note (int id, int note)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (note < 0 || note > 127)
-	return PATCH_NOTE_INVALID;
-
-    patches[id]->note = note;
-    return 0;
-}
-
-/* sets the panorama */
-int patch_set_panning (int id, float pan)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (pan < -1.0 || pan > 1.0)
-	return PATCH_PAN_INVALID;
-
-    patches[id]->pan.val = pan;
-    return 0;
-}
-
-/* set the pitch */
-int patch_set_pitch (int id, float pitch)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (pitch < -1.0 || pitch > 1.0)
-	return PATCH_PARAM_INVALID;
-
-    patches[id]->pitch.val = pitch;
-    return 0;
-}
-
-/* set the pitch range */
-int patch_set_pitch_steps (int id, int steps)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (steps < -PATCH_MAX_PITCH_STEPS
-	|| steps > PATCH_MAX_PITCH_STEPS)
-	return PATCH_PARAM_INVALID;
-
-    patches[id]->pitch_steps = steps;
-    return 0;
-}
-
-/* sets the play mode */
-int patch_set_play_mode (int id, PatchPlayMode mode)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    /* verify direction */
-    if (mode & PATCH_PLAY_FORWARD)
-    {
-	if (mode & PATCH_PLAY_REVERSE)
-	{
-	    return PATCH_PLAY_MODE_INVALID;
-	}
-    }
-    else if (mode & PATCH_PLAY_REVERSE)
-    {
-	if (mode & PATCH_PLAY_FORWARD)
-	{
-	    return PATCH_PLAY_MODE_INVALID;
-	}
-    }
-    else
-    {
-	return PATCH_PLAY_MODE_INVALID;
-    }
-
-    /* verify duration */
-    if (mode & PATCH_PLAY_SINGLESHOT)
-    {
-	if ((mode & PATCH_PLAY_TRIM) || (mode & PATCH_PLAY_LOOP))
-	{
-	    return PATCH_PLAY_MODE_INVALID;
-	}
-    }
-    else if (mode & PATCH_PLAY_TRIM)
-    {
-	if ((mode & PATCH_PLAY_SINGLESHOT) || (mode & PATCH_PLAY_LOOP))
-	{
-	    return PATCH_PLAY_MODE_INVALID;
-	}
-    }
-    else if (mode & PATCH_PLAY_LOOP)
-    {
-	if ((mode & PATCH_PLAY_SINGLESHOT) || (mode & PATCH_PLAY_TRIM))
-	{
-	    return PATCH_PLAY_MODE_INVALID;
-	}
-    }
-
-    /* make sure pingpong isn't frivolously set (just for style
-     * points) */
-    if ((mode & PATCH_PLAY_PINGPONG) && !(mode && PATCH_PLAY_LOOP))
-    {
-	return PATCH_PLAY_MODE_INVALID;
-    }
-
-    patches[id]->play_mode = mode;
-    return 0;
-}
-
-/* set whether portamento is being used or not */
-int patch_set_portamento (int id, bool val)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    patches[id]->porta = val;
-    return 0;
-}
-
-/* set length of portamento slides in seconds */
-int patch_set_portamento_time (int id, float secs)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (secs < 0.0)
-	return PATCH_PARAM_INVALID;
-
-    patches[id]->porta_secs = secs;
-    return 0;
-}
-
-
-/* set the filter's resonance */
-int patch_set_resonance (int id, float reso)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (reso < 0.0 || reso > 1.0)
-	return PATCH_PARAM_INVALID;
-
-    patches[id]->freso.val = reso;
-    return 0;
-}
-
-
-/* set the upper note of a patch's range */
-int patch_set_upper_note (int id, int note)
-{
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (note < 0 || note > 127)
-	return PATCH_NOTE_INVALID;
-
-    patches[id]->upper_note = note;
-    return 0;
-}
-
-/* set the amplitude */
-int patch_set_amplitude (int id, float vol)
-{
-
-    if (!isok (id))
-	return PATCH_ID_INVALID;
-
-    if (vol < 0 || vol > 1.0)
-	return PATCH_VOL_INVALID;
-
-    patches[id]->vol.val = vol;
-    return 0;
-}
-
-/**************************************************************************/
-/************************* PARAMETER GETTERS*******************************/
-/**************************************************************************/
-
-/* get the channel the patch listens on */
-int patch_get_channel (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->channel;
-}
-
-/* get the cut signal */
-int patch_get_cut (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->cut;
-}
-
-/* get the cut-by signal */
-int patch_get_cut_by (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->cut_by;
-}
-
-/* get the filter cutoff value */
-float patch_get_cutoff (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->ffreq.val;
-}
-
-/* get the display index */
-int patch_get_display_index (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->display_index;
-}
-
-/* get the number of frame in the sample */
-int patch_get_frames (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    if (patches[id]->sample->sp == NULL)
-        return 0;
-
-   debug("patches[%d].sample->frames:%d\n", id, patches[id]->sample->frames);
-
-    return patches[id]->sample->frames;
-}
-
-/* get whether this patch is played legato or not */
-bool patch_get_legato(int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->legato;
-}
-
-
-/* get the lower note */
-int patch_get_lower_note (int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->lower_note;
-}
-
-
-int patch_get_mark_frame(int patch, int mark)
-{
-    if (!isok(patch))
-        return -1;
-
-    if (patches[patch]->sample->sp == NULL)
-        return -1;
-
-    if (!mark_ok(mark))
-        return -1;
-
-    return get_mark_frame(patch, mark);
-}
-
-
-int patch_get_mark_frame_range(int patch, int mark, int* frame_min,
-                                                    int* frame_max)
-{
-    if (!isok(patch) || !mark_ok(mark))
-        return -1;
-
-    return get_mark_frame_range(patch, mark, frame_min, frame_max);
-}
-
-/* get whether this patch is monophonic or not */
-bool patch_get_monophonic(int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->mono;
-}
-
-/* get the name */
-char *patch_get_name (int id)
-{
-    char *name;
-
-    if (!isok(id))
-        name = strdup ("\0");
-    else
-        name = strdup (patches[id]->name);
-
-    return name;
-}
-
-/* get the root note */
-int patch_get_note (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->note;
-}
-
-/* get the panorama */
-float patch_get_panning (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->pan.val;
-}
-
-/* get the pitch */
-float patch_get_pitch (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->pitch.val;
-}
-
-/* get the pitch range */
-int patch_get_pitch_steps (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->pitch_steps;
-}
-
-/* get the play mode */
-PatchPlayMode patch_get_play_mode (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->play_mode;
-}
-
-/* get whether portamento is used or not */
-bool patch_get_portamento (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->porta;
-}
-
-/* get length of portamento slides in seconds */
-float patch_get_portamento_time (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->porta_secs;
-}
-
-
-/* get the filter's resonance amount */
-float patch_get_resonance (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->freso.val;
-}
-
-/* get a pointer to the sample data */
-const float *patch_get_sample (int id)
-{
-    if (!isok(id))
-        return NULL;
-
-    return patches[id]->sample->sp;
-}
-
-/* get the name of the sample file */
-const char *patch_get_sample_name (int id)
-{
-    if (!isok(id))
-        return 0;
-
-    return patches[id]->sample->filename;
-}
-
-/* get the upper note */
-int patch_get_upper_note (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->upper_note;
-}
-
-/* get the amplitude */
-float patch_get_amplitude (int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->vol.val;
-}
-
-
-int patch_get_fade_samples(int id)
-{
-    if (!isok (id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->fade_samples;
-}
-
-
-int patch_get_xfade_samples(int id)
-{
-    if (!isok(id))
-        return PATCH_ID_INVALID;
-
-    return patches[id]->xfade_samples;
-}
-
-
-int patch_get_max_fade_samples(int id)
-{
-    return (patches[id]->play_stop - patches[id]->play_start) / 2;
-}
-
-
-int patch_get_max_xfade_samples(int id)
-{
-    int min = patches[id]->sample->frames;
-    int tmp;
-
-    tmp = patches[id]->loop_stop - patches[id]->loop_start;
-    min = (tmp < min) ? tmp : min;
-
-    tmp = patches[id]->play_stop - patches[id]->loop_stop;
-    min = (tmp < min) ? tmp : min;
-
-    tmp = patches[id]->loop_start - patches[id]->play_start;
-    min = (tmp < min) ? tmp : min;
-
-debug("max xfade samples:%d\n", min);
-
-    return min;
-}
-
-
-/******************************************************************/
-/*************************** PARAM ********************************/
-/******************************************************************/
-
-int patch_param_get_value(int patch_id, PatchParamType param, float* v)
-{
-    if (!isok(patch_id))
-        return PATCH_ID_INVALID;
-
-    switch(param)
-    {
-    case PATCH_PARAM_AMPLITUDE: *v = patches[patch_id]->vol.val;    break;
-    case PATCH_PARAM_PANNING:   *v = patches[patch_id]->pan.val;    break;
-    case PATCH_PARAM_CUTOFF:    *v = patches[patch_id]->ffreq.val;  break;
-    case PATCH_PARAM_RESONANCE: *v = patches[patch_id]->freso.val;  break;
-    case PATCH_PARAM_PITCH:     *v = patches[patch_id]->pitch.val;  break;
-    default:
-        return PATCH_PARAM_INVALID;
-    }
-
-    return 0;
-}
-
-
-int patch_param_set_value(int patch_id, PatchParamType param, float v)
-{
-    if (!isok(patch_id))
-        return PATCH_ID_INVALID;
-
-    switch(param)
-    {
-    case PATCH_PARAM_AMPLITUDE: patches[patch_id]->vol.val = v;     break;
-    case PATCH_PARAM_PANNING:   patches[patch_id]->pan.val = v;     break;
-    case PATCH_PARAM_CUTOFF:    patches[patch_id]->ffreq.val = v;   break;
-    case PATCH_PARAM_RESONANCE: patches[patch_id]->freso.val = v;   break;
-    case PATCH_PARAM_PITCH:     patches[patch_id]->pitch.val = v;   break;
-    default:
-        return PATCH_PARAM_INVALID;
-    }
-
-    return 0;
-}
-
-
-/**************************************************************************/
-/*********************** MODULATION SETTERS *******************************/
-/**************************************************************************/
-
-int patch_set_mod_src(int patch_id, PatchParamType param, int slot, int id)
-{
-    PatchParam* p;
-    int err; 
-
-    if ((err = get_patch_param(patch_id, param, &p)) != 0)
-        return err;
-
-    if (slot < 0 || slot > MAX_MOD_SLOTS)
-        return PATCH_MOD_SLOT_INVALID;
-
-    if ((err = mod_src_ok(id)) != 0)
-        return err;
-
-    p->mod_id[slot] = id;
-    return 0;
-}
-
-
-int
-patch_set_mod_amt(int patch_id, PatchParamType param, int slot, float amt)
-{
-    PatchParam* p;
-    int err; 
-
-    if ((err = get_patch_param(patch_id, param, &p)) != 0)
-        return err;
-
-    if (slot < 0 || slot > MAX_MOD_SLOTS)
-        return PATCH_MOD_SLOT_INVALID;
-
-    if (amt < -1.0 || amt > 1.0)
-        return PATCH_MOD_AMOUNT_INVALID;
-
-    p->mod_amt[slot] = amt;
-
-    if (param == PATCH_PARAM_PITCH)
-    {
-        patches[patch_id]->mod_pitch_max[slot] =
-                        pow(2, (amt * PATCH_MAX_PITCH_STEPS) / 12.0);
-        patches[patch_id]->mod_pitch_min[slot] =
-                        pow(2, -(amt * PATCH_MAX_PITCH_STEPS) / 12.0);
-    }
-
-    return 0;
-}
-
-
-int patch_set_vel_amount(int patch_id, PatchParamType param, float amt)
-{
-    PatchParam* p;
-    int err;
-
-    if (!isok (patch_id))
-        return PATCH_ID_INVALID;
-
-    if ((err = get_patch_param(patch_id, param, &p)) < 0)
-        return err;
-
-    if (amt < 0.0 || amt > 1.0)
-        return PATCH_PARAM_INVALID;
-
-    p->vel_amt = amt;
-    return 0;
-}
-
-
-int patch_set_key_amount(int patch_id, PatchParamType param, float amt)
-{
-    PatchParam* p;
-    int err;
-
-    if (!isok (patch_id))
-        return PATCH_ID_INVALID;
-
-    if ((err = get_patch_param(patch_id, param, &p)) < 0)
-        return err;
-
-    if (amt < -1.0 || amt > 1.0)
-        return PATCH_PARAM_INVALID;
-
-    p->key_amt = amt;
-    return 0;
-}
-
-
-/**************************************************************************/
-/********************** MODULATION GETTERS ********************************/
-/**************************************************************************/
-
-int
-patch_get_mod_src(int patch_id, PatchParamType param, int slot, int* src_id)
-{
-    PatchParam* p;
-    int err; 
-
-    if ((err = get_patch_param(patch_id, param, &p)) != 0)
-        return err;
-
-    if (slot < 0 || slot > MAX_MOD_SLOTS)
-        return PATCH_MOD_SLOT_INVALID;
-
-    *src_id = p->mod_id[slot];
-
-    return 0;
-}
-
-
-int
-patch_get_mod_amt(int patch_id, PatchParamType param, int slot, float* amt)
-{
-    PatchParam* p;
-    int err; 
-
-    if ((err = get_patch_param(patch_id, param, &p)) != 0)
-        return err;
-
-    if (slot < 0 || slot > MAX_MOD_SLOTS)
-        return PATCH_MOD_SLOT_INVALID;
-
-    *amt = p->mod_amt[slot];
-    return 0;
-}
-
-
-int patch_get_vel_amount (int patch_id, PatchParamType param, float* val)
-{
-    PatchParam* p;
-    int err;
-     
-    if (!isok (patch_id))
-        return PATCH_ID_INVALID;
-
-    if ((err = get_patch_param(patch_id, param, &p)) < 0)
-        return err;
-
-    *val = p->vel_amt;
-    return 0;
-}
-
-
-int patch_get_key_amount (int patch_id, PatchParamType param, float* val)
-{
-    PatchParam* p;
-    int err;
-     
-    if (!isok (patch_id))
-        return PATCH_ID_INVALID;
-
-    if ((err = get_patch_param(patch_id, param, &p)) < 0)
-        return err;
-
-    *val = p->key_amt;
-    return 0;
-}
-
-
-/**************************************************************************/
-/****************** LFO FREQ MODULATION SETTERS ***************************/
-/**************************************************************************/
-
-int patch_set_lfo_fm1_src(int patch_id, int lfo_id, int modsrc_id)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->fm1_id = modsrc_id;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-int patch_set_lfo_fm2_src(int patch_id, int lfo_id, int modsrc_id)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->fm2_id = modsrc_id;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-int patch_set_lfo_fm1_amt(int patch_id, int lfo_id, float amount)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->fm1_amt = amount;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-int patch_set_lfo_fm2_amt(int patch_id, int lfo_id, float amount)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->fm2_amt = amount;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-
-
-/**************************************************************************/
-/****************** LFO FREQ MODULATION GETTERS ***************************/
-/**************************************************************************/
-
-int patch_get_lfo_fm1_src(int patch_id, int lfo_id, int* modsrc_id)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *modsrc_id = lfopar->fm1_id;
-    return 0;
-}
-
-int patch_get_lfo_fm2_src(int patch_id, int lfo_id, int* modsrc_id)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *modsrc_id = lfopar->fm2_id;
-    return 0;
-}
-
-int patch_get_lfo_fm1_amt(int patch_id, int lfo_id, float* amount)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *amount = lfopar->fm1_amt;
-    return 0;
-}
-
-int patch_get_lfo_fm2_amt(int patch_id, int lfo_id, float* amount)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *amount = lfopar->fm2_amt;
-    return 0;
-}
-
-
-/**************************************************************************/
-/******************* LFO AMP MODULATION SETTERS ***************************/
-/**************************************************************************/
-
-int patch_set_lfo_am1_src(int patch_id, int lfo_id, int modsrc_id)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->am1_id = modsrc_id;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-int patch_set_lfo_am2_src(int patch_id, int lfo_id, int modsrc_id)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->am2_id = modsrc_id;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-int patch_set_lfo_am1_amt(int patch_id, int lfo_id, float amount)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->am1_amt = amount;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-int patch_set_lfo_am2_amt(int patch_id, int lfo_id, float amount)
-{
-    LFO* lfo;
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, &lfo, &lfopar)))
-        return err;
-    lfopar->am2_amt = amount;
-
-    if (lfo)
-        patch_trigger_global_lfo(patch_id, lfo, lfopar);
-
-    return 0;
-}
-
-
-
-/**************************************************************************/
-/******************* LFO AMP MODULATION GETTERS ***************************/
-/**************************************************************************/
-
-int patch_get_lfo_am1_src(int patch_id, int lfo_id, int* modsrc_id)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *modsrc_id = lfopar->am1_id;
-    return 0;
-}
-
-int patch_get_lfo_am2_src(int patch_id, int lfo_id, int* modsrc_id)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *modsrc_id = lfopar->am2_id;
-    return 0;
-}
-
-int patch_get_lfo_am1_amt(int patch_id, int lfo_id, float* amount)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *amount = lfopar->am1_amt;
-    return 0;
-}
-
-int patch_get_lfo_am2_amt(int patch_id, int lfo_id, float* amount)
-{
-    LFOParams* lfopar;
-    int err; 
-    if ((err = lfo_from_id(patch_id, lfo_id, NULL, &lfopar)))
-        return err;
-    *amount = lfopar->am2_amt;
-    return 0;
-}
-
-
diff --git a/src/patch_set_and_get.h b/src/patch_set_and_get.h
deleted file mode 100644
index 6fd5ae5..0000000
--- a/src/patch_set_and_get.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/*  Petri-Foo is a fork of the Specimen audio sampler.
-
-    Original Specimen author Pete Bessman
-    Copyright 2005 Pete Bessman
-    Copyright 2011 James W. Morris
-
-    This file is part of Petri-Foo.
-
-    Petri-Foo is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License version 2 as
-    published by the Free Software Foundation.
-
-    Petri-Foo 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 Petri-Foo.  If not, see <http://www.gnu.org/licenses/>.
-
-    This file is a derivative of a Specimen original, modified 2011
-*/
-
-
-#ifndef __PATCH_SET_AND_GET_H__
-#define __PATCH_SET_AND_GET_H__
-
-
-#include "patch.h"
-
-
-/*  values to use as env_id and lfo_id are found in patch.h
-    under _MOD_SRC_ID_BITMASK.
-
-    to set the third envelope: env_id = MOD_SRC_EG + 3
- */
-
-/* envelope setters */
-int patch_set_env_on      (int patch_id, int env_id, bool state);
-int patch_set_env_delay   (int patch_id, int env_id, float secs);
-int patch_set_env_attack  (int patch_id, int env_id, float secs);
-int patch_set_env_hold    (int patch_id, int env_id, float secs);
-int patch_set_env_decay   (int patch_id, int env_id, float secs);
-int patch_set_env_sustain (int patch_id, int env_id, float level);
-int patch_set_env_release (int patch_id, int env_id, float secs);
-int patch_set_env_key_amt (int patch_id, int env_id, float val);
-int patch_set_env_vel_amt (int patch_id, int env_id, float val);
-
-/* envelope getters */
-int patch_get_env_on      (int patch_id, int env_id, bool* val);
-int patch_get_env_delay   (int patch_id, int env_id, float* val);
-int patch_get_env_attack  (int patch_id, int env_id, float* val);
-int patch_get_env_hold    (int patch_id, int env_id, float* val);
-int patch_get_env_decay   (int patch_id, int env_id, float* val);
-int patch_get_env_sustain (int patch_id, int env_id, float* val);
-int patch_get_env_release (int patch_id, int env_id, float* val);
-int patch_get_env_key_amt (int patch_id, int env_id, float* val);
-int patch_get_env_vel_amt (int patch_id, int env_id, float* val);
-
-/* lfo setters */
-int patch_set_lfo_on       (int patch_id, int lfo_id, bool state);
-int patch_set_lfo_attack   (int patch_id, int lfo_id, float secs);
-int patch_set_lfo_beats    (int patch_id, int lfo_id, float beats);
-int patch_set_lfo_delay    (int patch_id, int lfo_id, float secs);
-int patch_set_lfo_freq     (int patch_id, int lfo_id, float freq);
-int patch_set_lfo_positive (int patch_id, int lfo_id, bool state);
-int patch_set_lfo_shape    (int patch_id, int lfo_id, LFOShape shape);
-int patch_set_lfo_sync     (int patch_id, int lfo_id, bool state);
-
-/* lfo getters */
-int patch_get_lfo_on       (int patch_id, int lfo_id, bool* val);
-int patch_get_lfo_attack   (int patch_id, int lfo_id, float* secs);
-int patch_get_lfo_beats    (int patch_id, int lfo_id, float* val);
-int patch_get_lfo_delay    (int patch_id, int lfo_id, float* secs);
-int patch_get_lfo_freq     (int patch_id, int lfo_id, float* val);
-int patch_get_lfo_positive (int patch_id, int lfo_id, bool* val);
-int patch_get_lfo_shape    (int patch_id, int lfo_id, LFOShape* val);
-int patch_get_lfo_sync     (int patch_id, int lfo_id, bool* val);
-
-/* parameter setters */
-int patch_set_channel      (int id, int channel);
-int patch_set_cut          (int id, int cut);
-int patch_set_cut_by       (int id, int cut_by);
-int patch_set_cutoff       (int id, float freq);
-int patch_set_legato       (int id, bool val);
-int patch_set_lower_note   (int id, int note);
-
-/* both of these return mark_id on success */
-int patch_set_mark_frame   (int patch_id, int mark_id, int frame);
-
-/* returns mark_id on sucessful setting, if another mark required
-    moving to accomodate the set mark, the id of the moved mark will
-    be set via also_changed which is otherwise -1.
- */
-int patch_set_mark_frame_expand(int patch_id, int mark_id, int frame,
-                                                    int* also_changed);
-
-int patch_set_monophonic   (int id, bool val);
-int patch_set_name         (int id, const char* name);
-int patch_set_note         (int id, int note);
-int patch_set_panning      (int id, float pan);
-int patch_set_pitch        (int id, float pitch);
-int patch_set_pitch_steps  (int id, int steps);
-int patch_set_play_mode    (int id, PatchPlayMode mode);
-int patch_set_portamento   (int id, bool val);
-int patch_set_portamento_time(int id, float secs);
-int patch_set_range        (int id, bool range);
-int patch_set_resonance     (int id, float reso);
-
-int patch_set_upper_note   (int id, int note);
-int patch_set_amplitude    (int id, float vol);
-
-int patch_set_fade_samples (int id, int samples);
-int patch_set_xfade_samples(int id, int samples);
-
-/* parameter getters */
-int     patch_get_channel       (int id);
-int     patch_get_cut           (int id);
-int     patch_get_cut_by        (int id);
-float   patch_get_cutoff        (int id);
-int     patch_get_display_index (int id);
-int     patch_get_frames        (int id);
-bool    patch_get_legato        (int id);
-int     patch_get_lower_note    (int id);
-
-int     patch_get_mark_frame      (int patch_id, int mark_id);
-int     patch_get_mark_frame_range(int patch_id, int mark_id,
-                                                 int* frame_min,
-                                                 int* frame_max);
-
-bool            patch_get_monophonic    (int id);
-char*           patch_get_name          (int id);
-int             patch_get_note          (int id);
-float           patch_get_panning       (int id);
-float           patch_get_pitch         (int id);
-int             patch_get_pitch_steps   (int id);
-PatchPlayMode   patch_get_play_mode     (int id);
-bool            patch_get_portamento    (int id);
-float           patch_get_portamento_time(int id);
-
-/* still don't get the point of this:
-bool      patch_get_range         (int id);
-  seems to be TRUE when lower != upper.
- */
-
-float           patch_get_resonance     (int id);
-const float*    patch_get_sample        (int id);
-const char*     patch_get_sample_name   (int id);
-int             patch_get_upper_note    (int id);
-float           patch_get_amplitude     (int id);
-int             patch_get_fade_samples  (int id);
-int             patch_get_xfade_samples (int id);
-int             patch_get_max_fade_samples(int id);
-int             patch_get_max_xfade_samples(int id);
-
-
-/* returns 0 if non-raw sample loaded */
-int patch_get_raw_samplerate(int id);
-int patch_get_raw_channels(int id);
-int patch_get_raw_sndfile_format(int id);
-
-
-/* param */
-int patch_param_get_value(int patch_id, PatchParamType, float* val);
-int patch_param_set_value(int patch_id, PatchParamType, float  val);
-
-/* modulation setters */
-int patch_set_mod_src(int patch_id, PatchParamType, int slot, int src_id);
-int patch_set_mod_amt(int patch_id, PatchParamType, int slot, float amt);
-int patch_set_vel_amount(int id, PatchParamType param, float amt);
-int patch_set_key_amount(int id, PatchParamType param, float amt);
-
-/* modulation getters */
-int patch_get_mod_src(int patch_id, PatchParamType, int slot, int* src_id);
-int patch_get_mod_amt(int patch_id, PatchParamType, int slot, float* amt);
-int patch_get_vel_amount(int id, PatchParamType param, float* val);
-int patch_get_key_amount(int id, PatchParamType param, float* val);
-
-/* lfo freq modulation setters */
-int patch_set_lfo_fm1_src(int patch_id, int lfo_id, int modsrc_id);
-int patch_set_lfo_fm2_src(int patch_id, int lfo_id, int modsrc_id);
-int patch_set_lfo_fm1_amt(int patch_id, int lfo_id, float amount);
-int patch_set_lfo_fm2_amt(int patch_id, int lfo_id, float amount);
-
-/* lfo freq modulation getters */
-int patch_get_lfo_fm1_src(int patch_id, int lfo_id, int* modsrc_id);
-int patch_get_lfo_fm2_src(int patch_id, int lfo_id, int* modsrc_id);
-int patch_get_lfo_fm1_amt(int patch_id, int lfo_id, float* amount);
-int patch_get_lfo_fm2_amt(int patch_id, int lfo_id, float* amount);
-
-/* lfo amp modulation setters */
-int patch_set_lfo_am1_src(int patch_id, int lfo_id, int modsrc_id);
-int patch_set_lfo_am2_src(int patch_id, int lfo_id, int modsrc_id);
-int patch_set_lfo_am1_amt(int patch_id, int lfo_id, float amount);
-int patch_set_lfo_am2_amt(int patch_id, int lfo_id, float amount);
-
-/* lfo amp modulation getters */
-int patch_get_lfo_am1_src(int patch_id, int lfo_id, int* modsrc_id);
-int patch_get_lfo_am2_src(int patch_id, int lfo_id, int* modsrc_id);
-int patch_get_lfo_am1_amt(int patch_id, int lfo_id, float* amount);
-int patch_get_lfo_am2_amt(int patch_id, int lfo_id, float* amount);
-
-
-
-#endif /* __PATCH_SET_AND_GET_H__ */

-- 
petri-foo packaging



More information about the pkg-multimedia-commits mailing list