shithub: dumb

Download patch

ref: 61d10a21c142cb717bc25f03d5772b3cb4e09529
author: Chris Moeller <[email protected]>
date: Mon Jan 11 03:58:33 EST 2010

{10/11/2005 3:57:38 PM~10/11/2005 3:58:38 PM}

git-tfs-id: [http://localhost:8080/tfs/DefaultCollection/]$/foobar2000/files/plugins.root;C13

--- /dev/null
+++ b/dumb/include/dumb.h
@@ -1,0 +1,626 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * dumb.h - The user header file for DUMB.            / / \  \
+ *                                                   | <  /   \_
+ * Include this file in any of your files in         |  \/ /\   /
+ * which you wish to use the DUMB functions           \_  /  > /
+ * and variables.                                       | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#ifndef DUMB_H
+#define DUMB_H
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+
+#define DUMB_MAJOR_VERSION    0
+#define DUMB_MINOR_VERSION    9
+#define DUMB_REVISION_VERSION 2
+
+#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION)
+
+#define DUMB_VERSION_STR "0.9.2"
+
+#define DUMB_NAME "DUMB v"DUMB_VERSION_STR
+
+#define DUMB_YEAR  2003
+#define DUMB_MONTH 11
+#define DUMB_DAY   8
+
+#define DUMB_YEAR_STR2  "03"
+#define DUMB_YEAR_STR4  "2003"
+#define DUMB_MONTH_STR1 "11"
+#define DUMB_DAY_STR1   "8"
+
+#if DUMB_MONTH < 10
+#define DUMB_MONTH_STR2 "0"DUMB_MONTH_STR1
+#else
+#define DUMB_MONTH_STR2 DUMB_MONTH_STR1
+#endif
+
+#if DUMB_DAY < 10
+#define DUMB_DAY_STR2 "0"DUMB_DAY_STR1
+#else
+#define DUMB_DAY_STR2 DUMB_DAY_STR1
+#endif
+
+
+/* WARNING: The month and day were inadvertently swapped in the v0.8 release.
+ *          Please do not compare this constant against any date in 2002. In
+ *          any case, DUMB_VERSION is probably more useful for this purpose.
+ */
+#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY)
+
+#define DUMB_DATE_STR DUMB_DAY_STR1"."DUMB_MONTH_STR1"."DUMB_YEAR_STR4
+
+
+#undef MIN
+#undef MAX
+#undef MID
+
+#define MIN(x,y)   (((x) < (y)) ? (x) : (y))
+#define MAX(x,y)   (((x) > (y)) ? (x) : (y))
+#define MID(x,y,z) MAX((x), MIN((y), (z)))
+
+#undef ABS
+#define ABS(x) (((x) >= 0) ? (x) : (-(x)))
+
+
+#ifdef DEBUGMODE
+
+#ifndef ASSERT
+#include <assert.h>
+#define ASSERT(n) assert(n)
+#endif
+#ifndef TRACE
+// it would be nice if this did actually trace ...
+#define TRACE 1 ? (void)0 : (void)printf
+#endif
+
+#else
+
+#ifndef ASSERT
+#define ASSERT(n)
+#endif
+#ifndef TRACE
+#define TRACE 1 ? (void)0 : (void)printf
+#endif
+
+#endif
+
+
+#define DUMB_ID(a,b,c,d) (((unsigned int)(a) << 24) | \
+                          ((unsigned int)(b) << 16) | \
+                          ((unsigned int)(c) <<  8) | \
+                          ((unsigned int)(d)      ))
+
+
+
+#ifndef LONG_LONG
+#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__
+#define LONG_LONG long long
+#elif defined _MSC_VER || defined __WATCOMC__
+#define LONG_LONG __int64
+#elif defined __sgi
+#define LONG_LONG long long
+#else
+#error 64-bit integer type unknown
+#endif
+#endif
+
+#if __GNUC__ * 100 + __GNUC_MINOR__ >= 301 /* GCC 3.1+ */
+#ifndef DUMB_DECLARE_DEPRECATED
+#define DUMB_DECLARE_DEPRECATED
+#endif
+#define DUMB_DEPRECATED __attribute__((__deprecated__))
+#else
+#define DUMB_DEPRECATED
+#endif
+
+
+/* Basic Sample Type. Normal range is -0x800000 to 0x7FFFFF. */
+
+typedef int sample_t;
+
+
+/* Library Clean-up Management */
+
+int dumb_atexit(void (*proc)(void));
+
+void dumb_exit(void);
+
+
+/* File Input Functions */
+
+typedef struct DUMBFILE_SYSTEM
+{
+	void *(*open)(const char *filename);
+	int (*skip)(void *f, long n);
+	int (*getc)(void *f);
+	long (*getnc)(char *ptr, long n, void *f);
+	void (*close)(void *f);
+}
+DUMBFILE_SYSTEM;
+
+typedef struct DUMBFILE DUMBFILE;
+
+void register_dumbfile_system(const DUMBFILE_SYSTEM *dfs);
+
+DUMBFILE *dumbfile_open(const char *filename);
+DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs);
+
+long dumbfile_pos(DUMBFILE *f);
+int dumbfile_skip(DUMBFILE *f, long n);
+
+int dumbfile_getc(DUMBFILE *f);
+
+int dumbfile_igetw(DUMBFILE *f);
+int dumbfile_mgetw(DUMBFILE *f);
+
+long dumbfile_igetl(DUMBFILE *f);
+long dumbfile_mgetl(DUMBFILE *f);
+
+unsigned long dumbfile_cgetul(DUMBFILE *f);
+signed long dumbfile_cgetsl(DUMBFILE *f);
+
+long dumbfile_getnc(char *ptr, long n, DUMBFILE *f);
+
+int dumbfile_error(DUMBFILE *f);
+int dumbfile_close(DUMBFILE *f);
+
+
+/* stdio File Input Module */
+
+void dumb_register_stdfiles(void);
+
+DUMBFILE *dumbfile_open_stdfile(FILE *p);
+
+
+/* Memory File Input Module */
+
+DUMBFILE *dumbfile_open_memory(const char *data, long size);
+
+
+/* DUH Management */
+
+typedef struct DUH DUH;
+
+#define DUH_SIGNATURE DUMB_ID('D','U','H','!')
+
+void unload_duh(DUH *duh);
+
+DUH *load_duh(const char *filename);
+DUH *read_duh(DUMBFILE *f);
+
+long duh_get_length(DUH *duh);
+
+const char *duh_get_tag(DUH *duh, const char *key);
+
+
+/* Signal Rendering Functions */
+
+typedef struct DUH_SIGRENDERER DUH_SIGRENDERER;
+
+DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos);
+
+#ifdef DUMB_DECLARE_DEPRECATED
+typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long length);
+/* This is deprecated, but is not marked as such because GCC tends to
+ * complain spuriously when the typedef is used later. See comments below.
+ */
+
+void duh_sigrenderer_set_callback(
+	DUH_SIGRENDERER *sigrenderer,
+	DUH_SIGRENDERER_CALLBACK callback, void *data
+) DUMB_DEPRECATED;
+/* The 'callback' argument's type has changed for const-correctness. See the
+ * DUH_SIGRENDERER_CALLBACK definition just above. Also note that the samples
+ * in the buffer are now 256 times as large; the normal range is -0x800000 to
+ * 0x7FFFFF. The function has been renamed partly because its functionality
+ * has changed slightly and partly so that its name is more meaningful. The
+ * new one is duh_sigrenderer_set_analyser_callback(), and the typedef for
+ * the function pointer has also changed, from DUH_SIGRENDERER_CALLBACK to
+ * DUH_SIGRENDERER_ANALYSER_CALLBACK. (If you wanted to use this callback to
+ * apply a DSP effect, don't worry; there is a better way of doing this. It
+ * is undocumented, so contact me and I shall try to help. Contact details
+ * are in readme.txt.)
+ */
+#endif
+
+typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length);
+
+void duh_sigrenderer_set_analyser_callback(
+	DUH_SIGRENDERER *sigrenderer,
+	DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data
+);
+
+int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer);
+long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer);
+
+void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value);
+
+long duh_sigrenderer_get_samples(
+	DUH_SIGRENDERER *sigrenderer,
+	float volume, float delta,
+	long size, sample_t **samples
+);
+
+void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples);
+
+void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer);
+
+
+/* DUH Rendering Functions */
+
+long duh_render(
+	DUH_SIGRENDERER *sigrenderer,
+	int bits, int unsign,
+	float volume, float delta,
+	long size, void *sptr
+);
+
+#ifdef DUMB_DECLARE_DEPRECATED
+
+long duh_render_signal(
+	DUH_SIGRENDERER *sigrenderer,
+	float volume, float delta,
+	long size, sample_t **samples
+) DUMB_DEPRECATED;
+/* Please use duh_sigrenderer_get_samples(). Arguments and functionality are
+ * identical.
+ */
+
+typedef DUH_SIGRENDERER DUH_RENDERER DUMB_DEPRECATED;
+/* Please use DUH_SIGRENDERER instead of DUH_RENDERER. */
+
+DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) DUMB_DEPRECATED;
+/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */
+
+int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
+long duh_renderer_get_position(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
+/* Please use the duh_sigrenderer_*() equivalents of these two functions. */
+
+void duh_end_renderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
+/* Please use duh_end_sigrenderer() instead. */
+
+DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) DUMB_DEPRECATED;
+DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
+DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
+/* These functions have become no-ops that just return the parameter.
+ * So, for instance, replace
+ *   duh_renderer_encapsulate_sigrenderer(my_sigrenderer)
+ * with
+ *   my_sigrenderer
+ */
+
+#endif
+
+
+/* Impulse Tracker Support */
+
+extern int dumb_it_max_to_mix;
+
+typedef struct DUMB_IT_SIGDATA DUMB_IT_SIGDATA;
+typedef struct DUMB_IT_SIGRENDERER DUMB_IT_SIGRENDERER;
+
+DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh);
+DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos);
+DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer);
+
+int dumb_it_trim_silent_patterns(DUH * duh);
+
+typedef int (*dumb_scan_callback)(void *, int, long);
+int dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data);
+
+DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder);
+
+void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality);
+
+void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style);
+
+void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
+void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
+void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data);
+void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
+
+int dumb_it_callback_terminate(void *data);
+int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte);
+
+DUH *dumb_load_it(const char *filename);
+DUH *dumb_load_xm(const char *filename);
+DUH *dumb_load_s3m(const char *filename);
+DUH *dumb_load_mod(const char *filename);
+DUH *dumb_load_ptm(const char *filename);
+DUH *dumb_load_669(const char *filename);
+DUH *dumb_load_psm(const char *filename, int subsong);
+DUH *dumb_load_old_psm(const char * filename);
+DUH *dumb_load_mtm(const char *filename);
+
+DUH *dumb_read_it(DUMBFILE *f);
+DUH *dumb_read_xm(DUMBFILE *f);
+DUH *dumb_read_s3m(DUMBFILE *f);
+DUH *dumb_read_mod(DUMBFILE *f);
+DUH *dumb_read_ptm(DUMBFILE *f);
+DUH *dumb_read_669(DUMBFILE *f);
+DUH *dumb_read_psm(DUMBFILE *f, int subsong);
+DUH *dumb_read_old_psm(DUMBFILE *f);
+DUH *dumb_read_mtm(DUMBFILE *f);
+
+int dumb_get_psm_subsong_count(DUMBFILE *f);
+
+const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd);
+
+int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd);
+int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd);
+int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd);
+
+const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i);
+const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i);
+const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i);
+const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i);
+
+int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd);
+void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv);
+
+int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd);
+void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv);
+
+int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd);
+void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed);
+
+int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd);
+void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo);
+
+int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel);
+void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume);
+
+int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr);
+int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr);
+
+int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr);
+void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv);
+
+int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr);
+void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo);
+
+int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr);
+void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed);
+
+#define DUMB_IT_N_CHANNELS 64
+#define DUMB_IT_N_NNA_CHANNELS 192
+#define DUMB_IT_TOTAL_CHANNELS (DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS)
+
+/* Channels passed to any of these functions are 0-based */
+int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel);
+void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume);
+
+typedef struct DUMB_IT_CHANNEL_STATE DUMB_IT_CHANNEL_STATE;
+
+struct DUMB_IT_CHANNEL_STATE
+{
+	int channel; /* 0-based; meaningful for NNA channels */
+	int sample; /* 1-based; 0 if nothing playing, then other fields undef */
+	int freq; /* in Hz */
+	float volume; /* 1.0 maximum; affected by ALL factors, inc. mixing vol */
+	unsigned char pan; /* 0-64, 100 for surround */
+	signed char subpan; /* use (pan + subpan/256.0f) or ((pan<<8)+subpan) */
+	unsigned char filter_cutoff;    /* 0-127    cutoff=127 AND resonance=0 */
+	unsigned char filter_subcutoff; /* 0-255      -> no filters (subcutoff */
+	unsigned char filter_resonance; /* 0-127        always 0 in this case) */
+	/* subcutoff only changes from zero if filter envelopes are in use. The
+	 * calculation (filter_cutoff + filter_subcutoff/256.0f) gives a more
+	 * accurate filter cutoff measurement as a float. It would often be more
+	 * useful to use a scaled int such as ((cutoff<<8) + subcutoff).
+	 */
+};
+
+/* Values of 64 or more will access NNA channels here. */
+void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state);
+
+
+/* Signal Design Helper Values */
+
+/* Use pow(DUMB_SEMITONE_BASE, n) to get the 'delta' value to transpose up by
+ * n semitones. To transpose down, use negative n.
+ */
+#define DUMB_SEMITONE_BASE 1.059463094359295309843105314939748495817
+
+/* Use pow(DUMB_QUARTERTONE_BASE, n) to get the 'delta' value to transpose up
+ * by n quartertones. To transpose down, use negative n.
+ */
+#define DUMB_QUARTERTONE_BASE 1.029302236643492074463779317738953977823
+
+/* Use pow(DUMB_PITCH_BASE, n) to get the 'delta' value to transpose up by n
+ * units. In this case, 256 units represent one semitone; 3072 units
+ * represent one octave. These units are used by the sequence signal (SEQU).
+ */
+#define DUMB_PITCH_BASE 1.000225659305069791926712241547647863626
+
+
+/* Signal Design Function Types */
+
+typedef void sigdata_t;
+typedef void sigrenderer_t;
+
+typedef sigdata_t *(*DUH_LOAD_SIGDATA)(DUH *duh, DUMBFILE *file);
+
+typedef sigrenderer_t *(*DUH_START_SIGRENDERER)(
+	DUH *duh,
+	sigdata_t *sigdata,
+	int n_channels,
+	long pos
+);
+
+typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)(
+	sigrenderer_t *sigrenderer,
+	unsigned char id, long value
+);
+
+typedef long (*DUH_SIGRENDERER_GET_SAMPLES)(
+	sigrenderer_t *sigrenderer,
+	float volume, float delta,
+	long size, sample_t **samples
+);
+
+typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)(
+	sigrenderer_t *sigrenderer,
+	float volume,
+	sample_t *samples
+);
+
+typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer);
+
+typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata);
+
+
+/* Signal Design Function Registration */
+
+typedef struct DUH_SIGTYPE_DESC
+{
+	long type;
+	DUH_LOAD_SIGDATA                   load_sigdata;
+	DUH_START_SIGRENDERER              start_sigrenderer;
+	DUH_SIGRENDERER_SET_SIGPARAM       sigrenderer_set_sigparam;
+	DUH_SIGRENDERER_GET_SAMPLES        sigrenderer_get_samples;
+	DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample;
+	DUH_END_SIGRENDERER                end_sigrenderer;
+	DUH_UNLOAD_SIGDATA                 unload_sigdata;
+}
+DUH_SIGTYPE_DESC;
+
+void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc);
+
+
+// Decide where to put these functions; new heading?
+
+sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type);
+
+DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos);
+sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type);
+
+
+/* Standard Signal Types */
+
+void dumb_register_sigtype_sample(void);
+
+
+/* Sample Buffer Allocation Helpers */
+
+sample_t **create_sample_buffer(int n_channels, long length);
+void destroy_sample_buffer(sample_t **samples);
+
+
+/* Silencing Helper */
+
+void dumb_silence(sample_t *samples, long length);
+
+
+/* Click Removal Helpers */
+
+typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER;
+
+DUMB_CLICK_REMOVER *dumb_create_click_remover(void);
+void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step);
+void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, float halflife);
+sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr);
+void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr);
+
+DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n);
+void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step);
+void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step);
+void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife);
+void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset);
+void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr);
+
+
+/* Resampling Helpers */
+
+#define DUMB_RQ_ALIASING 0
+#define DUMB_RQ_LINEAR   1
+#define DUMB_RQ_CUBIC    2
+#define DUMB_RQ_N_LEVELS 3
+extern int dumb_resampling_quality;
+
+typedef struct DUMB_RESAMPLER DUMB_RESAMPLER;
+
+typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
+
+struct DUMB_RESAMPLER
+{
+	void *src;
+	long pos;
+	int subpos;
+	long start, end;
+	int dir;
+	DUMB_RESAMPLE_PICKUP pickup;
+	void *pickup_data;
+	int quality;
+	/* Everything below this point is internal: do not use. */
+	union {
+		sample_t x24[3];
+		short x16[3];
+		signed char x8[3];
+	} x;
+	int overshot;
+};
+
+void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, long pos, long start, long end, int quality);
+DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, long pos, long start, long end, int quality);
+long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float * volume, float volume_delta, float volume_target, float volume_mix, float delta);
+sample_t dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, float volume);
+void dumb_end_resampler(DUMB_RESAMPLER *resampler);
+
+void dumb_reset_resampler_16(DUMB_RESAMPLER *resampler, short *src, long pos, long start, long end, int quality);
+DUMB_RESAMPLER *dumb_start_resampler_16(short *src, long pos, long start, long end, int quality);
+long dumb_resample_16(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float * volume, float volume_delta, float volume_target, float volume_mix, float delta);
+sample_t dumb_resample_get_current_sample_16(DUMB_RESAMPLER *resampler, float volume);
+void dumb_end_resampler_16(DUMB_RESAMPLER *resampler);
+
+void dumb_reset_resampler_8(DUMB_RESAMPLER *resampler, signed char *src, long pos, long start, long end, int quality);
+DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, long pos, long start, long end, int quality);
+long dumb_resample_8(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float * volume, float volume_delta, float volume_target, float volume_mix, float delta);
+sample_t dumb_resample_get_current_sample_8(DUMB_RESAMPLER *resampler, float volume);
+void dumb_end_resampler_8(DUMB_RESAMPLER *resampler);
+
+void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, long pos, long start, long end, int quality);
+DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, long pos, long start, long end, int quality);
+long dumb_resample_n(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float * volume, float volume_delta, float volume_target, float volume_mix, float delta);
+sample_t dumb_resample_get_current_sample_n(int n, DUMB_RESAMPLER *resampler, float volume);
+void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler);
+
+
+/* DUH Construction */
+
+DUH *make_duh(
+	long length,
+	int n_tags,
+	const char *const tag[][2],
+	int n_signals,
+	DUH_SIGTYPE_DESC *desc[],
+	sigdata_t *sigdata[]
+);
+
+
+#ifdef __cplusplus
+	}
+#endif
+
+
+#endif /* DUMB_H */
--- /dev/null
+++ b/dumb/include/internal/dumb.h
@@ -1,0 +1,60 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * internal/dumb.h - DUMB's internal declarations.    / / \  \
+ *                                                   | <  /   \_
+ * This header file provides access to the           |  \/ /\   /
+ * internal structure of DUMB, and is liable          \_  /  > /
+ * to change, mutate or cease to exist at any           | \ / /
+ * moment. Include it at your own peril.                |  ' /
+ *                                                       \__/
+ * ...
+ *
+ * I mean it, people. You don't need access to anything in this file. If you
+ * disagree, contact the authors. In the unlikely event that you make a good
+ * case, we'll add what you need to dumb.h. Thanking you kindly.
+ */
+
+#ifndef INTERNAL_DUMB_H
+#define INTERNAL_DUMB_H
+
+
+typedef struct DUH_SIGTYPE_DESC_LINK
+{
+	struct DUH_SIGTYPE_DESC_LINK *next;
+	DUH_SIGTYPE_DESC *desc;
+}
+DUH_SIGTYPE_DESC_LINK;
+
+
+typedef struct DUH_SIGNAL
+{
+	sigdata_t *sigdata;
+	DUH_SIGTYPE_DESC *desc;
+}
+DUH_SIGNAL;
+
+
+struct DUH
+{
+	long length;
+
+	int n_tags;
+	char *(*tag)[2];
+
+	int n_signals;
+	DUH_SIGNAL **signal;
+};
+
+
+DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type);
+
+
+#endif /* INTERNAL_DUMB_H */
--- /dev/null
+++ b/dumb/include/internal/it.h
@@ -1,0 +1,861 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * internal/it.h - Internal stuff for IT playback     / / \  \
+ *                 and MOD/XM/S3M conversion.        | <  /   \_
+ *                                                   |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#ifndef INTERNAL_IT_H
+#define INTERNAL_IT_H
+
+
+#define BIT_ARRAY_BULLSHIT
+
+#include <stddef.h>
+
+#include "barray.h"
+
+
+/** TO DO: THINK ABOUT THE FOLLOWING:
+
+sigdata->flags & IT_COMPATIBLE_GXX
+
+                Bit 5: On = Link Effect G's memory with Effect E/F. Also
+                            Gxx with an instrument present will cause the
+                            envelopes to be retriggered. If you change a
+                            sample on a row with Gxx, it'll adjust the
+                            frequency of the current note according to:
+
+                              NewFrequency = OldFrequency * NewC5 / OldC5;
+*/
+
+
+
+/* These #defines are TEMPORARY. They are used to write alternative code to
+ * handle ambiguities in the format specification. The correct code in each
+ * case will be determined most likely by experimentation.
+ */
+#define STEREO_SAMPLES_COUNT_AS_TWO
+#define INVALID_ORDERS_END_SONG
+#define INVALID_NOTES_CAUSE_NOTE_CUT
+#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP
+#define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
+
+
+
+#define SIGTYPE_IT DUMB_ID('I', 'T', ' ', ' ')
+
+#define IT_SIGNATURE            DUMB_ID('I', 'M', 'P', 'M')
+#define IT_INSTRUMENT_SIGNATURE DUMB_ID('I', 'M', 'P', 'I')
+#define IT_SAMPLE_SIGNATURE     DUMB_ID('I', 'M', 'P', 'S')
+
+
+
+/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get
+ * the interval between ticks.
+ */
+#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6))
+
+
+
+/* I'm not going to try to explain this, because I didn't derive it very
+ * formally ;)
+ */
+/* #define AMIGA_DIVISOR ((float)(4.0 * 14317056.0)) */
+/* I believe the following one to be more accurate. */
+//#define AMIGA_DIVISOR ((float)(8.0 * 7159090.5))
+#define AMIGA_DIVISOR ((float)(16.0 * 3579546.471))
+
+
+
+typedef struct IT_MIDI IT_MIDI;
+typedef struct IT_FILTER_STATE IT_FILTER_STATE;
+typedef struct IT_ENVELOPE IT_ENVELOPE;
+typedef struct IT_INSTRUMENT IT_INSTRUMENT;
+typedef struct IT_SAMPLE IT_SAMPLE;
+typedef struct IT_ENTRY IT_ENTRY;
+typedef struct IT_PATTERN IT_PATTERN;
+typedef struct IT_PLAYING_ENVELOPE IT_PLAYING_ENVELOPE;
+typedef struct IT_PLAYING IT_PLAYING;
+typedef struct IT_CHANNEL IT_CHANNEL;
+typedef struct IT_CHECKPOINT IT_CHECKPOINT;
+typedef struct IT_CALLBACKS IT_CALLBACKS;
+
+
+
+struct IT_MIDI
+{
+	unsigned char SFmacro[16][16]; // read these from 0x120
+	unsigned char SFmacrolen[16];
+	unsigned short SFmacroz[16]; /* Bitfield; bit 0 set = z in first position */
+	unsigned char Zmacro[128][16]; // read these from 0x320
+	unsigned char Zmacrolen[128];
+};
+
+
+
+struct IT_FILTER_STATE
+{
+	float currsample, prevsample;
+};
+
+
+
+#define IT_ENVELOPE_ON                1
+#define IT_ENVELOPE_LOOP_ON           2
+#define IT_ENVELOPE_SUSTAIN_LOOP      4
+#define IT_ENVELOPE_CARRY             8
+#define IT_ENVELOPE_PITCH_IS_FILTER 128
+
+struct IT_ENVELOPE
+{
+	unsigned char flags;
+	unsigned char n_nodes;
+	unsigned char loop_start;
+	unsigned char loop_end;
+	unsigned char sus_loop_start;
+	unsigned char sus_loop_end;
+	signed char node_y[25];
+	unsigned short node_t[25];
+};
+
+
+
+#define NNA_NOTE_CUT      0
+#define NNA_NOTE_CONTINUE 1
+#define NNA_NOTE_OFF      2
+#define NNA_NOTE_FADE     3
+
+#define DCT_OFF        0
+#define DCT_NOTE       1
+#define DCT_SAMPLE     2
+#define DCT_INSTRUMENT 3
+
+#define DCA_NOTE_CUT  0
+#define DCA_NOTE_OFF  1
+#define DCA_NOTE_FADE 2
+
+struct IT_INSTRUMENT
+{
+	unsigned char name[27];
+	unsigned char filename[14];
+
+	int fadeout;
+
+	IT_ENVELOPE volume_envelope;
+	IT_ENVELOPE pan_envelope;
+	IT_ENVELOPE pitch_envelope;
+
+	unsigned char new_note_action;
+	unsigned char dup_check_type;
+	unsigned char dup_check_action;
+	signed char pp_separation;
+	unsigned char pp_centre;
+	unsigned char global_volume;
+	unsigned char default_pan;
+	unsigned char random_volume;
+	unsigned char random_pan;
+
+	unsigned char filter_cutoff;
+	unsigned char filter_resonance;
+
+	unsigned char map_note[120];
+	unsigned short map_sample[120];
+};
+
+
+
+#define IT_SAMPLE_EXISTS              1
+#define IT_SAMPLE_16BIT               2
+#define IT_SAMPLE_STEREO              4
+#define IT_SAMPLE_LOOP               16
+#define IT_SAMPLE_SUS_LOOP           32
+#define IT_SAMPLE_PINGPONG_LOOP      64
+#define IT_SAMPLE_PINGPONG_SUS_LOOP 128
+
+#define IT_VIBRATO_SINE      0
+#define IT_VIBRATO_SAWTOOTH  1
+#define IT_VIBRATO_SQUARE    2
+#define IT_VIBRATO_RANDOM    3
+#define IT_VIBRATO_XM_SQUARE 4
+#define IT_VIBRATO_RAMP_DOWN 5
+#define IT_VIBRATO_RAMP_UP   6
+
+struct IT_SAMPLE
+{
+	unsigned char name[35];
+	unsigned char filename[14];
+	unsigned char flags;
+	unsigned char global_volume;
+	unsigned char default_volume;
+	unsigned char default_pan;
+	/* default_pan:
+	 *   0-255 for XM
+	 *   ignored for MOD
+	 *   otherwise, 0-64, and add 128 to enable
+	 */
+
+	long length;
+	long loop_start;
+	long loop_end;
+	long C5_speed;
+	long sus_loop_start;
+	long sus_loop_end;
+
+	unsigned char vibrato_speed;
+	unsigned char vibrato_depth;
+	unsigned char vibrato_rate;
+	unsigned char vibrato_waveform;
+
+	void *left;
+	void *right;
+
+	int max_resampling_quality;
+};
+
+
+
+#define IT_ENTRY_NOTE       1
+#define IT_ENTRY_INSTRUMENT 2
+#define IT_ENTRY_VOLPAN     4
+#define IT_ENTRY_EFFECT     8
+
+#define IT_SET_END_ROW(entry) ((entry)->channel = 255)
+#define IT_IS_END_ROW(entry) ((entry)->channel >= DUMB_IT_N_CHANNELS)
+
+#define IT_NOTE_OFF 255
+#define IT_NOTE_CUT 254
+
+#define IT_ENVELOPE_SHIFT 8
+
+#define IT_SURROUND 100
+#define IT_IS_SURROUND(pan) ((pan) > 64)
+#define IT_IS_SURROUND_SHIFTED(pan) ((pan) > 64 << IT_ENVELOPE_SHIFT)
+
+#define IT_SET_SPEED              1
+#define IT_JUMP_TO_ORDER          2
+#define IT_BREAK_TO_ROW           3
+#define IT_VOLUME_SLIDE           4
+#define IT_PORTAMENTO_DOWN        5
+#define IT_PORTAMENTO_UP          6
+#define IT_TONE_PORTAMENTO        7
+#define IT_VIBRATO                8
+#define IT_TREMOR                 9
+#define IT_ARPEGGIO              10
+#define IT_VOLSLIDE_VIBRATO      11
+#define IT_VOLSLIDE_TONEPORTA    12
+#define IT_SET_CHANNEL_VOLUME    13
+#define IT_CHANNEL_VOLUME_SLIDE  14
+#define IT_SET_SAMPLE_OFFSET     15
+#define IT_PANNING_SLIDE         16
+#define IT_RETRIGGER_NOTE        17
+#define IT_TREMOLO               18
+#define IT_S                     19
+#define IT_SET_SONG_TEMPO        20
+#define IT_FINE_VIBRATO          21
+#define IT_SET_GLOBAL_VOLUME     22
+#define IT_GLOBAL_VOLUME_SLIDE   23
+#define IT_SET_PANNING           24
+#define IT_PANBRELLO             25
+#define IT_MIDI_MACRO            26 //see MIDI.TXT
+
+/* Some effects needed for XM compatibility */
+#define IT_XM_PORTAMENTO_DOWN    27
+#define IT_XM_PORTAMENTO_UP      28
+#define IT_XM_FINE_VOLSLIDE_DOWN 29
+#define IT_XM_FINE_VOLSLIDE_UP   30
+#define IT_XM_RETRIGGER_NOTE     31
+#define IT_XM_KEY_OFF            32
+
+/* More effects needed for PTM compatibility */
+#define IT_PTM_NOTE_SLIDE_DOWN   33
+#define IT_PTM_NOTE_SLIDE_UP     34
+#define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 35
+#define IT_PTM_NOTE_SLIDE_UP_RETRIG 36
+
+#define IT_N_EFFECTS             37
+
+/* These represent the top nibble of the command value. */
+#define IT_S_SET_FILTER              0 /* Greyed out in IT... */
+#define IT_S_SET_GLISSANDO_CONTROL   1 /* Greyed out in IT... */
+#define IT_S_FINETUNE                2 /* Greyed out in IT... */
+#define IT_S_SET_VIBRATO_WAVEFORM    3
+#define IT_S_SET_TREMOLO_WAVEFORM    4
+#define IT_S_SET_PANBRELLO_WAVEFORM  5
+#define IT_S_FINE_PATTERN_DELAY      6
+#define IT_S7                        7
+#define IT_S_SET_PAN                 8
+#define IT_S_SET_SURROUND_SOUND      9
+#define IT_S_SET_HIGH_OFFSET        10
+#define IT_S_PATTERN_LOOP           11
+#define IT_S_DELAYED_NOTE_CUT       12
+#define IT_S_NOTE_DELAY             13
+#define IT_S_PATTERN_DELAY          14
+#define IT_S_SET_MIDI_MACRO         15
+
+/*
+S0x Set filter
+S1x Set glissando control
+S2x Set finetune
+
+
+S3x Set vibrato waveform to type x
+S4x Set tremelo waveform to type x
+S5x Set panbrello waveform to type x
+  Waveforms for commands S3x, S4x and S5x:
+    0: Sine wave
+    1: Ramp down
+    2: Square wave
+    3: Random wave
+S6x Pattern delay for x ticks
+S70 Past note cut
+S71 Past note off
+S72 Past note fade
+S73 Set NNA to note cut
+S74 Set NNA to continue
+S75 Set NNA to note off
+S76 Set NNA to note fade
+S77 Turn off volume envelope
+S78 Turn on volume envelope
+S79 Turn off panning envelope
+S7A Turn on panning envelope
+S7B Turn off pitch envelope
+S7C Turn on pitch envelope
+S8x Set panning position
+S91 Set surround sound
+SAy Set high value of sample offset yxx00h
+SB0 Set loopback point
+SBx Loop x times to loopback point
+SCx Note cut after x ticks
+SDx Note delay for x ticks
+SEx Pattern delay for x rows
+SFx Set parameterised MIDI Macro
+*/
+
+struct IT_ENTRY
+{
+	unsigned char channel; /* End of row if channel >= DUMB_IT_N_CHANNELS */
+	unsigned char mask;
+	unsigned char note;
+	unsigned char instrument;
+	unsigned char volpan;
+	unsigned char effect;
+	unsigned char effectvalue;
+};
+
+
+
+struct IT_PATTERN
+{
+	int n_rows;
+	int n_entries;
+	IT_ENTRY *entry;
+};
+
+
+
+#define IT_STEREO            1
+#define IT_USE_INSTRUMENTS   4
+#define IT_LINEAR_SLIDES     8 /* If not set, use Amiga slides */
+#define IT_OLD_EFFECTS      16
+#define IT_COMPATIBLE_GXX   32
+
+/* Make sure IT_WAS_AN_XM and IT_WAS_A_MOD aren't set accidentally */
+#define IT_REAL_FLAGS       63
+
+#define IT_WAS_AN_XM        64 /* Set for both XMs and MODs */
+#define IT_WAS_A_MOD       128
+
+#define IT_WAS_AN_S3M      256
+
+#define IT_WAS_A_PTM       512
+
+#define IT_WAS_A_669      1024
+
+#define IT_ORDER_END  255
+#define IT_ORDER_SKIP 254
+
+struct DUMB_IT_SIGDATA
+{
+	unsigned char name[60];
+
+	unsigned char *song_message;
+
+	int n_orders;
+	int n_instruments;
+	int n_samples;
+	int n_patterns;
+	int n_pchannels;
+
+	int flags;
+
+	int global_volume;
+	int mixing_volume;
+	int speed;
+	int tempo;
+	int pan_separation;
+
+	unsigned char channel_pan[DUMB_IT_N_CHANNELS];
+	unsigned char channel_volume[DUMB_IT_N_CHANNELS];
+
+	unsigned char *order;
+	unsigned char restart_position; /* for XM compatiblity */
+
+	IT_INSTRUMENT *instrument;
+	IT_SAMPLE *sample;
+	IT_PATTERN *pattern;
+
+	IT_MIDI *midi;
+
+	IT_CHECKPOINT *checkpoint;
+};
+
+
+
+struct IT_PLAYING_ENVELOPE
+{
+	int next_node;
+	int tick;
+	int value;
+};
+
+
+
+#define IT_PLAYING_BACKGROUND 1
+#define IT_PLAYING_SUSTAINOFF 2
+#define IT_PLAYING_FADING     4
+#define IT_PLAYING_DEAD       8
+
+struct IT_PLAYING
+{
+	int flags;
+
+	int resampling_quality;
+
+	IT_CHANNEL *channel;
+	IT_SAMPLE *sample;
+	IT_INSTRUMENT *instrument;
+	IT_INSTRUMENT *env_instrument;
+
+	unsigned short sampnum;
+	unsigned char instnum;
+
+	unsigned char declick_stage;
+	float declick_volume;
+
+	float float_volume[2];
+	float ramp_volume[2];
+	float ramp_delta[2];
+
+	unsigned char channel_volume;
+
+	unsigned char volume;
+	unsigned short pan;
+
+	signed char volume_offset, panning_offset;
+
+	unsigned char note;
+
+	unsigned char enabled_envelopes;
+
+	unsigned char filter_cutoff;
+	unsigned char filter_resonance;
+
+	unsigned short true_filter_cutoff;   /* These incorporate the filter envelope, and will not */
+	unsigned char true_filter_resonance; /* be changed if they would be set to 127<<8 and 0.    */
+
+	unsigned char vibrato_speed;
+	unsigned char vibrato_depth;
+	unsigned char vibrato_n; /* May be specified twice: volpan & effect. */
+	unsigned char vibrato_time;
+	unsigned char vibrato_waveform;
+
+	unsigned char tremolo_speed;
+	unsigned char tremolo_depth;
+	unsigned char tremolo_time;
+	unsigned char tremolo_waveform;
+
+	unsigned char panbrello_speed;
+	unsigned char panbrello_depth;
+	unsigned char panbrello_time;
+	unsigned char panbrello_waveform;
+	signed char panbrello_random;
+
+	unsigned char sample_vibrato_time;
+	unsigned char sample_vibrato_waveform;
+	int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */
+
+	int slide;
+	float delta;
+	int finetune;
+
+	IT_PLAYING_ENVELOPE volume_envelope;
+	IT_PLAYING_ENVELOPE pan_envelope;
+	IT_PLAYING_ENVELOPE pitch_envelope;
+
+	int fadeoutcount;
+
+	IT_FILTER_STATE filter_state[2]; /* Left and right */
+
+	DUMB_RESAMPLER resampler[2];
+
+	/* time_lost is used to emulate Impulse Tracker's sample looping
+	 * characteristics. When time_lost is added to pos, the result represents
+	 * the position in the theoretical version of the sample where all loops
+	 * have been expanded. If this is stored, the resampling helpers will
+	 * safely convert it for use with new loop boundaries. The situation is
+	 * slightly more complicated if dir == -1 when the change takes place; we
+	 * must reflect pos off the loop end point and set dir to 1 before
+	 * proceeding.
+	 */
+	long time_lost;
+};
+
+
+
+#define IT_CHANNEL_MUTED 1
+
+#define IT_ENV_VOLUME  1
+#define IT_ENV_PANNING 2
+#define IT_ENV_PITCH   4
+
+struct IT_CHANNEL
+{
+	int flags;
+
+	unsigned char volume;
+	signed char volslide;
+	signed char xm_volslide;
+	signed char panslide;
+
+	/* xm_volslide is used for volume slides done in the volume column in an
+	 * XM file, since it seems the volume column slide is applied first,
+	 * followed by clamping, followed by the effects column slide. IT does
+	 * not exhibit this behaviour, so xm_volslide is maintained at zero.
+	 */
+
+	unsigned char pan;
+	unsigned short truepan;
+
+	unsigned char channelvolume;
+	signed char channelvolslide;
+
+	unsigned char instrument;
+	unsigned char note;
+
+	unsigned char SFmacro;
+
+	unsigned char filter_cutoff;
+	unsigned char filter_resonance;
+
+	unsigned char key_off_count;
+	unsigned char note_cut_count;
+	unsigned char note_delay_count;
+	IT_ENTRY *note_delay_entry;
+
+	unsigned char new_note_action;
+
+	int arpeggio;
+	unsigned char retrig;
+	unsigned char xm_retrig;
+	int retrig_tick;
+
+	unsigned char tremor;
+	unsigned char tremor_time; /* Bit 6 set if note on; bit 7 set if tremor active. */
+
+	unsigned char vibrato_waveform;
+	unsigned char tremolo_waveform;
+	unsigned char panbrello_waveform;
+
+	int portamento;
+	int toneporta;
+	int toneslide;
+	unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide;
+	unsigned char destnote;
+	unsigned char toneslide_retrig;
+
+	unsigned char glissando;
+
+	/** WARNING - for neatness, should one or both of these be in the IT_PLAYING struct? */
+	unsigned short sample;
+	unsigned char truenote;
+
+	unsigned char midi_state;
+
+	signed char lastvolslide;
+	unsigned char lastDKL;
+	unsigned char lastEF; /* Doubles as last portamento up for XM files */
+	unsigned char lastG;
+	unsigned char lastHspeed;
+	unsigned char lastHdepth;
+	unsigned char lastRspeed;
+	unsigned char lastRdepth;
+	unsigned char lastYspeed;
+	unsigned char lastYdepth;
+	unsigned char lastI;
+	unsigned char lastJ; /* Doubles as last portamento down for XM files */
+	unsigned char lastN;
+	unsigned char lastO;
+	unsigned char high_offset;
+	unsigned char lastP;
+	unsigned char lastQ;
+	unsigned char lastS;
+	unsigned char pat_loop_row;
+	unsigned char pat_loop_count;
+	unsigned char lastW;
+
+	unsigned char xm_lastE1;
+	unsigned char xm_lastE2;
+	unsigned char xm_lastEA;
+	unsigned char xm_lastEB;
+	unsigned char xm_lastX1;
+	unsigned char xm_lastX2;
+
+	IT_PLAYING *playing;
+
+#ifdef BIT_ARRAY_BULLSHIT
+	void * played_patjump;
+	int played_patjump_order;
+#endif
+};
+
+
+
+struct DUMB_IT_SIGRENDERER
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	int n_channels;
+
+	int resampling_quality;
+
+	unsigned char globalvolume;
+	signed char globalvolslide;
+
+	unsigned char tempo;
+	signed char temposlide;
+
+	IT_CHANNEL channel[DUMB_IT_N_CHANNELS];
+
+	IT_PLAYING *playing[DUMB_IT_N_NNA_CHANNELS];
+
+	int tick;
+	int speed;
+	int rowcount;
+
+	int order; /* Set to -1 if the song is terminated by a callback. */
+	int row;
+	int processorder;
+	int processrow;
+	int breakrow;
+
+	int restart_position;
+
+	int n_rows;
+
+	IT_ENTRY *entry_start;
+	IT_ENTRY *entry;
+	IT_ENTRY *entry_end;
+
+	long time_left; /* Time before the next tick is processed */
+	int sub_time_left;
+
+	DUMB_CLICK_REMOVER **click_remover;
+
+	IT_CALLBACKS *callbacks;
+
+#ifdef BIT_ARRAY_BULLSHIT
+	/* bit array, which rows are played, only checked by pattern break or loop commands */
+	void * played;
+#endif
+
+	long gvz_time;
+	int gvz_sub_time;
+
+	int ramp_style;
+};
+
+
+
+struct IT_CHECKPOINT
+{
+	IT_CHECKPOINT *next;
+	long time;
+	DUMB_IT_SIGRENDERER *sigrenderer;
+};
+
+
+
+struct IT_CALLBACKS
+{
+	int (*loop)(void *data);
+	void *loop_data;
+	/* Return 1 to prevent looping; the music will terminate abruptly. If you
+	 * want to make the music stop but allow samples to fade (beware, as they
+	 * might not fade at all!), use dumb_it_sr_set_speed() and set the speed
+	 * to 0. Note that xm_speed_zero() will not be called if you set the
+	 * speed manually, and also that this will work for IT and S3M files even
+	 * though the music can't stop in this way by itself.
+	 */
+
+	int (*xm_speed_zero)(void *data);
+	void *xm_speed_zero_data;
+	/* Return 1 to terminate the mod, without letting samples fade. */
+
+	int (*midi)(void *data, int channel, unsigned char byte);
+	void *midi_data;
+	/* Return 1 to prevent DUMB from subsequently interpreting the MIDI bytes
+	 * itself. In other words, return 1 if the Zxx macros in an IT file are
+	 * controlling filters and shouldn't be.
+	 */
+
+	int (*global_volume_zero)(void *data);
+	void *global_volume_zero_data;
+	/* Return 1 to terminate the module when global volume is set to zero. */
+};
+
+
+
+void _dumb_it_end_sigrenderer(sigrenderer_t *sigrenderer);
+void _dumb_it_unload_sigdata(sigdata_t *vsigdata);
+
+extern DUH_SIGTYPE_DESC _dumb_sigtype_it;
+
+
+
+long _dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder);
+
+
+
+#define XM_APPREGIO                0
+#define XM_PORTAMENTO_UP           1
+#define XM_PORTAMENTO_DOWN         2
+#define XM_TONE_PORTAMENTO         3
+#define XM_VIBRATO                 4
+#define XM_VOLSLIDE_TONEPORTA      5
+#define XM_VOLSLIDE_VIBRATO        6
+#define XM_TREMOLO                 7
+#define XM_SET_PANNING             8
+#define XM_SAMPLE_OFFSET           9
+#define XM_VOLUME_SLIDE            10 /* A */
+#define XM_POSITION_JUMP           11 /* B */
+#define XM_SET_CHANNEL_VOLUME      12 /* C */
+#define XM_PATTERN_BREAK           13 /* D */
+#define XM_E                       14 /* E */
+#define XM_SET_TEMPO_BPM           15 /* F */
+#define XM_SET_GLOBAL_VOLUME       16 /* G */
+#define XM_GLOBAL_VOLUME_SLIDE     17 /* H */
+#define XM_KEY_OFF                 20 /* K (undocumented) */
+#define XM_SET_ENVELOPE_POSITION   21 /* L */
+#define XM_PANNING_SLIDE           25 /* P */
+#define XM_MULTI_RETRIG            27 /* R */
+#define XM_TREMOR                  29 /* T */
+#define XM_X                       33 /* X */
+#define XM_N_EFFECTS               (10+26)
+
+#define XM_E_SET_FILTER            0x0
+#define XM_E_FINE_PORTA_UP         0x1
+#define XM_E_FINE_PORTA_DOWN       0x2
+#define XM_E_SET_GLISSANDO_CONTROL 0x3
+#define XM_E_SET_VIBRATO_CONTROL   0x4
+#define XM_E_SET_FINETUNE          0x5
+#define XM_E_SET_LOOP              0x6
+#define XM_E_SET_TREMOLO_CONTROL   0x7
+#define XM_E_SET_PANNING           0x8
+#define XM_E_RETRIG_NOTE           0x9
+#define XM_E_FINE_VOLSLIDE_UP      0xA
+#define XM_E_FINE_VOLSLIDE_DOWN    0xB
+#define XM_E_NOTE_CUT              0xC
+#define XM_E_NOTE_DELAY            0xD
+#define XM_E_PATTERN_DELAY         0xE
+
+#define XM_X_EXTRAFINE_PORTA_UP    1
+#define XM_X_EXTRAFINE_PORTA_DOWN  2
+
+/* To make my life a bit simpler during conversion, effect E:xy is converted
+ * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That
+ * way, these effects can be manipulated like regular effects.
+ */
+#define EBASE              (XM_N_EFFECTS)
+#define XBASE              (EBASE+16)
+#define SBASE              (IT_N_EFFECTS)
+
+#define EFFECT_VALUE(x, y) (((x)<<4)|(y))
+#define HIGH(v)            ((v)>>4)
+#define LOW(v)             ((v)&0x0F)
+#define SET_HIGH(v, x)     v = (((x)<<4)|((v)&0x0F))
+#define SET_LOW(v, y)      v = (((v)&0xF0)|(y))
+#define BCD_TO_NORMAL(v)   (HIGH(v)*10+LOW(v))
+
+
+
+#if 0
+unsigned char **_dumb_malloc2(int w, int h);
+void _dumb_free2(unsigned char **line);
+#endif
+
+void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod);
+int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata);
+
+
+#define PTM_APPREGIO                0
+#define PTM_PORTAMENTO_UP           1
+#define PTM_PORTAMENTO_DOWN         2
+#define PTM_TONE_PORTAMENTO         3
+#define PTM_VIBRATO                 4
+#define PTM_VOLSLIDE_TONEPORTA      5
+#define PTM_VOLSLIDE_VIBRATO        6
+#define PTM_TREMOLO                 7
+#define PTM_SAMPLE_OFFSET           9
+#define PTM_VOLUME_SLIDE            10 /* A */
+#define PTM_POSITION_JUMP           11 /* B */
+#define PTM_SET_CHANNEL_VOLUME      12 /* C */
+#define PTM_PATTERN_BREAK           13 /* D */
+#define PTM_E                       14 /* E */
+#define PTM_SET_TEMPO_BPM           15 /* F */
+#define PTM_SET_GLOBAL_VOLUME       16 /* G */
+#define PTM_RETRIGGER               17 /* H */
+#define PTM_FINE_VIBRATO            18 /* I */
+#define PTM_NOTE_SLIDE_UP           19 /* J */
+#define PTM_NOTE_SLIDE_DOWN         20 /* K */
+#define PTM_NOTE_SLIDE_UP_RETRIG    21 /* L */
+#define PTM_NOTE_SLIDE_DOWN_RETRIG  22 /* M */
+#define PTM_N_EFFECTS               23
+
+#define PTM_E_FINE_PORTA_DOWN       0x1
+#define PTM_E_FINE_PORTA_UP         0x2
+#define PTM_E_SET_VIBRATO_CONTROL   0x4
+#define PTM_E_SET_FINETUNE          0x5
+#define PTM_E_SET_LOOP              0x6
+#define PTM_E_SET_TREMOLO_CONTROL   0x7
+#define PTM_E_SET_PANNING           0x8
+#define PTM_E_RETRIG_NOTE           0x9
+#define PTM_E_FINE_VOLSLIDE_UP      0xA
+#define PTM_E_FINE_VOLSLIDE_DOWN    0xB
+#define PTM_E_NOTE_CUT              0xC
+#define PTM_E_NOTE_DELAY            0xD
+#define PTM_E_PATTERN_DELAY         0xE
+
+/* To make my life a bit simpler during conversion, effect E:xy is converted
+ * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That
+ * way, these effects can be manipulated like regular effects.
+ */
+#define PTM_EBASE              (PTM_N_EFFECTS)
+
+void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry);
+
+long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f);
+
+#endif /* INTERNAL_IT_H */
--- /dev/null
+++ b/dumb/src/core/atexit.c
@@ -1,0 +1,71 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * atexit.c - Library Clean-up Management.            / / \  \
+ *                                                   | <  /   \_
+ * By entheh.                                        |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+typedef struct DUMB_ATEXIT_PROC
+{
+	struct DUMB_ATEXIT_PROC *next;
+	void (*proc)(void);
+}
+DUMB_ATEXIT_PROC;
+
+
+
+static DUMB_ATEXIT_PROC *dumb_atexit_proc = NULL;
+
+
+
+int dumb_atexit(void (*proc)(void))
+{
+	DUMB_ATEXIT_PROC *dap = dumb_atexit_proc;
+
+	while (dap) {
+		if (dap->proc == proc) return 0;
+		dap = dap->next;
+	}
+
+	dap = malloc(sizeof(*dap));
+
+	if (!dap)
+		return -1;
+
+	dap->next = dumb_atexit_proc;
+	dap->proc = proc;
+	dumb_atexit_proc = dap;
+
+	return 0;
+}
+
+
+
+void dumb_exit(void)
+{
+	while (dumb_atexit_proc) {
+		DUMB_ATEXIT_PROC *next = dumb_atexit_proc->next;
+		(*dumb_atexit_proc->proc)();
+		free(dumb_atexit_proc);
+		dumb_atexit_proc = next;
+	}
+}
--- /dev/null
+++ b/dumb/src/core/duhlen.c
@@ -1,0 +1,34 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * duhlen.c - Function to return the length of        / / \  \
+ *            a DUH.                                 | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ * Note that the length of a DUH is a constant          |  ' /
+ * stored in the DUH struct and in the DUH disk          \__/
+ * format. It will be calculated on loading for
+ * other formats in which the length is not explicitly stored. Also note that
+ * it does not necessarily correspond to the length of time for which the DUH
+ * will generate samples. Rather it represents a suitable point for a player
+ * such as Winamp to stop, and in any good DUH it will allow for any final
+ * flourish to fade out and be appreciated.
+ */
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+long duh_get_length(DUH *duh)
+{
+	return duh ? duh->length : 0;
+}
--- /dev/null
+++ b/dumb/src/core/duhtag.c
@@ -1,0 +1,38 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * duhtag.c - Function to return the tags stored      / / \  \
+ *            in a DUH struct (typically author      | <  /   \_
+ *            information).                          |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+const char *duh_get_tag(DUH *duh, const char *key)
+{
+	int i;
+	ASSERT(key);
+	if (!duh || !duh->tag) return NULL;
+
+	for (i = 0; i < duh->n_tags; i++)
+		if (strcmp(key, duh->tag[i][0]) == 0)
+			return duh->tag[i][1];
+
+	return NULL;
+}
--- /dev/null
+++ b/dumb/src/core/dumbfile.c
@@ -1,0 +1,401 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * dumbfile.c - Hookable, strictly sequential         / / \  \
+ *              file input functions.                | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+
+
+
+static DUMBFILE_SYSTEM *the_dfs = NULL;
+
+
+
+void register_dumbfile_system(const DUMBFILE_SYSTEM *dfs)
+{
+	ASSERT(dfs);
+	ASSERT(dfs->open);
+	ASSERT(dfs->getc);
+	ASSERT(dfs->close);
+	the_dfs = dfs;
+}
+
+
+
+struct DUMBFILE
+{
+	DUMBFILE_SYSTEM *dfs;
+	void *file;
+	long pos;
+};
+
+
+
+DUMBFILE *dumbfile_open(const char *filename)
+{
+	DUMBFILE *f;
+
+	ASSERT(the_dfs);
+
+	f = malloc(sizeof(*f));
+
+	if (!f)
+		return NULL;
+
+	f->dfs = the_dfs;
+
+	f->file = (*the_dfs->open)(filename);
+
+	if (!f->file) {
+		free(f);
+		return NULL;
+	}
+
+	f->pos = 0;
+
+	return f;
+}
+
+
+
+DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs)
+{
+	DUMBFILE *f;
+
+	ASSERT(dfs);
+	ASSERT(dfs->getc);
+	ASSERT(file);
+
+	f = malloc(sizeof(*f));
+
+	if (!f) {
+		if (dfs->close)
+			(*dfs->close)(file);
+		return NULL;
+	}
+
+	f->dfs = dfs;
+	f->file = file;
+
+	f->pos = 0;
+
+	return f;
+}
+
+
+
+long dumbfile_pos(DUMBFILE *f)
+{
+	ASSERT(f);
+
+	return f->pos;
+}
+
+
+
+int dumbfile_skip(DUMBFILE *f, long n)
+{
+	int rv;
+
+	ASSERT(f);
+	ASSERT(n >= 0);
+
+	if (f->pos < 0)
+		return -1;
+
+	f->pos += n;
+
+	if (f->dfs->skip) {
+		rv = (*f->dfs->skip)(f->file, n);
+		if (rv) {
+			f->pos = -1;
+			return rv;
+		}
+	} else {
+		while (n) {
+			rv = (*f->dfs->getc)(f->file);
+			if (rv < 0) {
+				f->pos = -1;
+				return rv;
+			}
+			n--;
+		}
+	}
+
+	return 0;
+}
+
+
+
+int dumbfile_getc(DUMBFILE *f)
+{
+	int rv;
+
+	ASSERT(f);
+
+	if (f->pos < 0)
+		return -1;
+
+	rv = (*f->dfs->getc)(f->file);
+
+	if (rv < 0) {
+		f->pos = -1;
+		return rv;
+	}
+
+	f->pos++;
+
+	return rv;
+}
+
+
+
+int dumbfile_igetw(DUMBFILE *f)
+{
+	int l, h;
+
+	ASSERT(f);
+
+	if (f->pos < 0)
+		return -1;
+
+	l = (*f->dfs->getc)(f->file);
+	if (l < 0) {
+		f->pos = -1;
+		return l;
+	}
+
+	h = (*f->dfs->getc)(f->file);
+	if (h < 0) {
+		f->pos = -1;
+		return h;
+	}
+
+	f->pos += 2;
+
+	return l | (h << 8);
+}
+
+
+
+int dumbfile_mgetw(DUMBFILE *f)
+{
+	int l, h;
+
+	ASSERT(f);
+
+	if (f->pos < 0)
+		return -1;
+
+	h = (*f->dfs->getc)(f->file);
+	if (h < 0) {
+		f->pos = -1;
+		return h;
+	}
+
+	l = (*f->dfs->getc)(f->file);
+	if (l < 0) {
+		f->pos = -1;
+		return l;
+	}
+
+	f->pos += 2;
+
+	return l | (h << 8);
+}
+
+
+
+long dumbfile_igetl(DUMBFILE *f)
+{
+	unsigned long rv, b;
+
+	ASSERT(f);
+
+	if (f->pos < 0)
+		return -1;
+
+	rv = (*f->dfs->getc)(f->file);
+	if ((signed long)rv < 0) {
+		f->pos = -1;
+		return rv;
+	}
+
+	b = (*f->dfs->getc)(f->file);
+	if ((signed long)b < 0) {
+		f->pos = -1;
+		return b;
+	}
+	rv |= b << 8;
+
+	b = (*f->dfs->getc)(f->file);
+	if ((signed long)b < 0) {
+		f->pos = -1;
+		return b;
+	}
+	rv |= b << 16;
+
+	b = (*f->dfs->getc)(f->file);
+	if ((signed long)b < 0) {
+		f->pos = -1;
+		return b;
+	}
+	rv |= b << 24;
+
+	f->pos += 4;
+
+	return rv;
+}
+
+
+
+long dumbfile_mgetl(DUMBFILE *f)
+{
+	unsigned long rv, b;
+
+	ASSERT(f);
+
+	if (f->pos < 0)
+		return -1;
+
+	rv = (*f->dfs->getc)(f->file);
+	if ((signed long)rv < 0) {
+		f->pos = -1;
+		return rv;
+	}
+	rv <<= 24;
+
+	b = (*f->dfs->getc)(f->file);
+	if ((signed long)b < 0) {
+		f->pos = -1;
+		return b;
+	}
+	rv |= b << 16;
+
+	b = (*f->dfs->getc)(f->file);
+	if ((signed long)b < 0) {
+		f->pos = -1;
+		return b;
+	}
+	rv |= b << 8;
+
+	b = (*f->dfs->getc)(f->file);
+	if ((signed long)b < 0) {
+		f->pos = -1;
+		return b;
+	}
+	rv |= b;
+
+	f->pos += 4;
+
+	return rv;
+}
+
+
+
+unsigned long dumbfile_cgetul(DUMBFILE *f)
+{
+	unsigned long rv = 0;
+	int v;
+
+	do {
+		v = dumbfile_getc(f);
+
+		if (v < 0)
+			return v;
+
+		rv <<= 7;
+		rv |= v & 0x7F;
+	} while (v & 0x80);
+
+	return rv;
+}
+
+
+
+signed long dumbfile_cgetsl(DUMBFILE *f)
+{
+	unsigned long rv = dumbfile_cgetul(f);
+
+	if (f->pos < 0)
+		return rv;
+
+	return (rv >> 1) | (rv << 31);
+}
+
+
+
+long dumbfile_getnc(char *ptr, long n, DUMBFILE *f)
+{
+	long rv;
+
+	ASSERT(f);
+	ASSERT(n >= 0);
+
+	if (f->pos < 0)
+		return -1;
+
+	if (f->dfs->getnc) {
+		rv = (*f->dfs->getnc)(ptr, n, f->file);
+		if (rv < n) {
+			f->pos = -1;
+			return MAX(rv, 0);
+		}
+	} else {
+		for (rv = 0; rv < n; rv++) {
+			int c = (*f->dfs->getc)(f->file);
+			if (c < 0) {
+				f->pos = -1;
+				return rv;
+			}
+			*ptr++ = c;
+		}
+	}
+
+	f->pos += rv;
+
+	return rv;
+}
+
+
+
+int dumbfile_error(DUMBFILE *f)
+{
+	ASSERT(f);
+
+	return f->pos < 0;
+}
+
+
+
+int dumbfile_close(DUMBFILE *f)
+{
+	int rv;
+
+	ASSERT(f);
+
+	rv = f->pos < 0;
+
+	if (f->dfs->close)
+		(*f->dfs->close)(f->file);
+
+	free(f);
+
+	return rv;
+}
--- /dev/null
+++ b/dumb/src/core/loadduh.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loadduh.c - Code to read a DUH from a file,        / / \  \
+ *             opening and closing the file for      | <  /   \_
+ *             you.                                  |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+/* load_duh(): loads a .duh file, returning a pointer to a DUH struct.
+ * When you have finished with it, you must pass the pointer to unload_duh()
+ * so that the memory can be freed.
+ */
+DUH *load_duh(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = read_duh(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/core/makeduh.c
@@ -1,0 +1,132 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * makeduh.c - Function to construct a DUH from       / / \  \
+ *             its components.                       | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static DUH_SIGNAL *make_signal(DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata)
+{
+	DUH_SIGNAL *signal;
+
+	ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer));
+	ASSERT(desc->sigrenderer_get_samples && desc->sigrenderer_get_current_sample);
+
+	signal = malloc(sizeof(*signal));
+
+	if (!signal) {
+		if (desc->unload_sigdata)
+			if (sigdata)
+				(*desc->unload_sigdata)(sigdata);
+		return NULL;
+	}
+
+	signal->desc = desc;
+	signal->sigdata = sigdata;
+
+	return signal;
+}
+
+
+
+DUH *make_duh(
+	long length,
+	int n_tags,
+	const char *const tags[][2],
+	int n_signals,
+	DUH_SIGTYPE_DESC *desc[],
+	sigdata_t *sigdata[]
+)
+{
+	DUH *duh = malloc(sizeof(*duh));
+	int i;
+	int fail;
+
+	if (duh) {
+		duh->n_signals = n_signals;
+
+		duh->signal = malloc(n_signals * sizeof(*duh->signal));
+
+		if (!duh->signal) {
+			free(duh);
+			duh = NULL;
+		}
+	}
+
+	if (!duh) {
+		for (i = 0; i < n_signals; i++)
+			if (desc[i]->unload_sigdata)
+				if (sigdata[i])
+					(*desc[i]->unload_sigdata)(sigdata[i]);
+		return NULL;
+	}
+
+	duh->n_tags = 0;
+	duh->tag = NULL;
+
+	fail = 0;
+
+	for (i = 0; i < n_signals; i++) {
+		duh->signal[i] = make_signal(desc[i], sigdata[i]);
+		if (!duh->signal[i])
+			fail = 1;
+	}
+
+	if (fail) {
+		unload_duh(duh);
+		return NULL;
+	}
+
+	duh->length = length;
+
+	{
+		int mem = n_tags * 2; /* account for NUL terminators here */
+		char *ptr;
+
+		for (i = 0; i < n_tags; i++)
+			mem += strlen(tags[i][0]) + strlen(tags[i][1]);
+
+		if (mem <= 0) return duh;
+
+		duh->tag = malloc(n_tags * sizeof(*duh->tag));
+		if (!duh->tag) return duh;
+		duh->tag[0][0] = malloc(mem);
+		if (!duh->tag[0][0]) {
+			free(duh->tag);
+			duh->tag = NULL;
+			return duh;
+		}
+		duh->n_tags = n_tags;
+		ptr = duh->tag[0][0];
+		for (i = 0; i < n_tags; i++) {
+			duh->tag[i][0] = ptr;
+			strcpy(ptr, tags[i][0]);
+			ptr += strlen(tags[i][0]) + 1;
+			duh->tag[i][1] = ptr;
+			strcpy(ptr, tags[i][1]);
+			ptr += strlen(tags[i][1]) + 1;
+		}
+	}
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/core/rawsig.c
@@ -1,0 +1,44 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * rawsig.c - Function to retrieve raw signal         / / \  \
+ *            data from a DUH provided you know      | <  /   \_
+ *            what type of signal it is.             |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+/* You have to specify the type of sigdata, proving you know what to do with
+ * the pointer. If you get it wrong, you can expect NULL back.
+ */
+sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type)
+{
+	DUH_SIGNAL *signal;
+
+	if (!duh) return NULL;
+
+	if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL;
+
+	signal = duh->signal[sig];
+
+	if (signal && signal->desc->type == type)
+		return signal->sigdata;
+
+	return NULL;
+}
--- /dev/null
+++ b/dumb/src/core/readduh.c
@@ -1,0 +1,107 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readduh.c - Code to read a DUH from an open        / / \  \
+ *             file.                                 | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static DUH_SIGNAL *read_signal(DUH *duh, DUMBFILE *f)
+{
+	DUH_SIGNAL *signal;
+	long type;
+
+	signal = malloc(sizeof(*signal));
+
+	if (!signal)
+		return NULL;
+
+	type = dumbfile_mgetl(f);
+	if (dumbfile_error(f)) {
+		free(signal);
+		return NULL;
+	}
+
+	signal->desc = _dumb_get_sigtype_desc(type);
+	if (!signal->desc) {
+		free(signal);
+		return NULL;
+	}
+
+	if (signal->desc->load_sigdata) {
+		signal->sigdata = (*signal->desc->load_sigdata)(duh, f);
+		if (!signal->sigdata) {
+			free(signal);
+			return NULL;
+		}
+	} else
+		signal->sigdata = NULL;
+
+	return signal;
+}
+
+
+
+/* read_duh(): reads a DUH from an already open DUMBFILE, and returns its
+ * pointer, or null on error. The file is not closed.
+ */
+DUH *read_duh(DUMBFILE *f)
+{
+	DUH *duh;
+	int i;
+
+	if (dumbfile_mgetl(f) != DUH_SIGNATURE)
+		return NULL;
+
+	duh = malloc(sizeof(*duh));
+	if (!duh)
+		return NULL;
+
+	duh->length = dumbfile_igetl(f);
+	if (dumbfile_error(f) || duh->length <= 0) {
+		free(duh);
+		return NULL;
+	}
+
+	duh->n_signals = dumbfile_igetl(f);
+	if (dumbfile_error(f) || duh->n_signals <= 0) {
+		free(duh);
+		return NULL;
+	}
+
+	duh->signal = malloc(sizeof(*duh->signal) * duh->n_signals);
+	if (!duh->signal) {
+		free(duh);
+		return NULL;
+	}
+
+	for (i = 0; i < duh->n_signals; i++)
+		duh->signal[i] = NULL;
+
+	for (i = 0; i < duh->n_signals; i++) {
+		if (!(duh->signal[i] = read_signal(duh, f))) {
+			unload_duh(duh);
+			return NULL;
+		}
+	}
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/core/register.c
@@ -1,0 +1,104 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * register.c - Signal type registration.             / / \  \
+ *                                                   | <  /   \_
+ * By entheh.                                        |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static DUH_SIGTYPE_DESC_LINK *sigtype_desc = NULL;
+static DUH_SIGTYPE_DESC_LINK **sigtype_desc_tail = &sigtype_desc;
+
+
+
+/* destroy_sigtypes(): frees all memory allocated while registering signal
+ * types. This function is set up to be called by dumb_exit().
+ */
+static void destroy_sigtypes(void)
+{
+	DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc, *next;
+	sigtype_desc = NULL;
+	sigtype_desc_tail = &sigtype_desc;
+
+	while (desc_link) {
+		next = desc_link->next;
+		free(desc_link);
+		desc_link = next;
+	}
+}
+
+
+
+/* dumb_register_sigtype(): registers a new signal type with DUMB. The signal
+ * type is identified by a four-character string (e.g. "WAVE"), which you can
+ * encode using the the DUMB_ID() macro (e.g. DUMB_ID('W','A','V','E')). The
+ * signal's behaviour is defined by four functions, whose pointers you pass
+ * here. See the documentation for details.
+ *
+ * If a DUH tries to use a signal that has not been registered using this
+ * function, then the library will fail to load the DUH.
+ */
+void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc)
+{
+	DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc;
+
+	ASSERT((desc->load_sigdata && desc->unload_sigdata) || (!desc->load_sigdata && !desc->unload_sigdata));
+	ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer));
+	ASSERT(desc->sigrenderer_get_samples && desc->sigrenderer_get_current_sample);
+
+	if (desc_link) {
+		do {
+			if (desc_link->desc->type == desc->type) {
+				desc_link->desc = desc;
+				return;
+			}
+			desc_link = desc_link->next;
+		} while (desc_link);
+	} else
+		dumb_atexit(&destroy_sigtypes);
+
+	desc_link = *sigtype_desc_tail = malloc(sizeof(DUH_SIGTYPE_DESC_LINK));
+
+	if (!desc_link)
+		return;
+
+	desc_link->next = NULL;
+	sigtype_desc_tail = &desc_link->next;
+
+	desc_link->desc = desc;
+}
+
+
+
+/* _dumb_get_sigtype_desc(): searches the registered functions for a signal
+ * type matching the parameter. If such a sigtype is found, it returns a
+ * pointer to a sigtype descriptor containing the necessary functions to
+ * manage the signal. If none is found, it returns NULL.
+ */
+DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type)
+{
+	DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc;
+
+	while (desc_link && desc_link->desc->type != type)
+		desc_link = desc_link->next;
+
+	return desc_link ? desc_link->desc : NULL;
+}
--- /dev/null
+++ b/dumb/src/core/rendduh.c
@@ -1,0 +1,202 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * rendduh.c - Functions for rendering a DUH into     / / \  \
+ *             an end-user sample format.            | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <limits.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+/* On the x86, we can use some tricks to speed stuff up */
+#if (defined _MSC_VER) || (defined __DJGPP__) || (defined __MINGW__)
+// Can't we detect Linux and other x86 platforms here? :/
+
+#define FAST_MID(var, min, max) {                  \
+	var -= (min);                                  \
+	var &= (~var) >> (sizeof(var) * CHAR_BIT - 1); \
+	var += (min);                                  \
+	var -= (max);                                  \
+	var &= var >> (sizeof(var) * CHAR_BIT - 1);    \
+	var += (max);                                  \
+}
+
+#define CONVERT8(src, pos, signconv) {       \
+	signed int f = (src + 0x8000) >> 16;     \
+	FAST_MID(f, -128, 127);                  \
+	((char*)sptr)[pos] = (char)f ^ signconv; \
+}
+
+#define CONVERT16(src, pos, signconv) {          \
+	signed int f = (src + 0x80) >> 8;            \
+	FAST_MID(f, -32768, 32767);                  \
+	((short*)sptr)[pos] = (short)(f ^ signconv); \
+}
+
+#else
+
+#define CONVERT8(src, pos, signconv)		  \
+{											  \
+	signed int f = (src + 0x8000) >> 16;	  \
+	f = MID(-128, f, 127);					  \
+	((char *)sptr)[pos] = (char)f ^ signconv; \
+}
+
+
+
+#define CONVERT16(src, pos, signconv)			  \
+{												  \
+	signed int f = (src + 0x80) >> 8;			  \
+	f = MID(-32768, f, 32767);					  \
+	((short *)sptr)[pos] = (short)(f ^ signconv); \
+}
+
+#endif
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos)
+{
+	return duh_start_sigrenderer(duh, 0, n_channels, pos);
+}
+
+
+
+long duh_render(
+	DUH_SIGRENDERER *sigrenderer,
+	int bits, int unsign,
+	float volume, float delta,
+	long size, void *sptr
+)
+{
+	long n;
+
+	sample_t **sampptr;
+
+	int n_channels;
+
+	ASSERT(bits == 8 || bits == 16);
+	ASSERT(sptr);
+
+	if (!sigrenderer)
+		return 0;
+
+	n_channels = duh_sigrenderer_get_n_channels(sigrenderer);
+
+	ASSERT(n_channels > 0);
+	/* This restriction will be removed when need be. At the moment, tightly
+	 * optimised loops exist for exactly one or two channels.
+	 */
+	ASSERT(n_channels <= 2);
+
+	sampptr = create_sample_buffer(n_channels, size);
+
+	if (!sampptr)
+		return 0;
+
+	dumb_silence(sampptr[0], n_channels * size);
+
+	size = duh_sigrenderer_get_samples(sigrenderer, volume, delta, size, sampptr);
+
+	if (bits == 16) {
+		int signconv = unsign ? 0x8000 : 0x0000;
+
+		if (n_channels == 2) {
+			for (n = 0; n < size; n++) {
+				CONVERT16(sampptr[0][n], n << 1, signconv);
+			}
+			for (n = 0; n < size; n++) {
+				CONVERT16(sampptr[1][n], (n << 1) + 1, signconv);
+			}
+		} else {
+			for (n = 0; n < size; n++) {
+				CONVERT16(sampptr[0][n], n, signconv);
+			}
+		}
+	} else {
+		char signconv = unsign ? 0x80 : 0x00;
+
+		if (n_channels == 2) {
+			for (n = 0; n < size; n++) {
+				CONVERT8(sampptr[0][n], n << 1, signconv);
+			}
+			for (n = 0; n < size; n++) {
+				CONVERT8(sampptr[1][n], (n << 1) + 1, signconv);
+			}
+		} else {
+			for (n = 0; n < size; n++) {
+				CONVERT8(sampptr[0][n], n, signconv);
+			}
+		}
+	}
+
+	destroy_sample_buffer(sampptr);
+
+	return size;
+}
+
+
+
+/* DEPRECATED */
+int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr)
+{
+	return duh_sigrenderer_get_n_channels(dr);
+}
+
+
+
+/* DEPRECATED */
+long duh_renderer_get_position(DUH_SIGRENDERER *dr)
+{
+	return duh_sigrenderer_get_position(dr);
+}
+
+
+
+/* DEPRECATED */
+void duh_end_renderer(DUH_SIGRENDERER *dr)
+{
+	duh_end_sigrenderer(dr);
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer)
+{
+	return sigrenderer;
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr)
+{
+	return dr;
+}
+
+
+
+/* DEPRECATED */
+DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr)
+{
+	return dr;
+}
--- /dev/null
+++ b/dumb/src/core/rendsig.c
@@ -1,0 +1,301 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * rendsig.c - Wrappers to render samples from        / / \  \
+ *             the signals in a DUH.                 | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+struct DUH_SIGRENDERER
+{
+	DUH_SIGTYPE_DESC *desc;
+
+	sigrenderer_t *sigrenderer;
+
+	int n_channels;
+
+	long pos;
+	int subpos;
+
+	DUH_SIGRENDERER_ANALYSER_CALLBACK callback;
+	void *callback_data;
+};
+
+
+
+DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos)
+{
+	DUH_SIGRENDERER *sigrenderer;
+
+	DUH_SIGNAL *signal;
+	DUH_START_SIGRENDERER proc;
+
+	if (!duh)
+		return NULL;
+
+	if ((unsigned int)sig >= (unsigned int)duh->n_signals)
+		return NULL;
+
+	signal = duh->signal[sig];
+	if (!signal)
+		return NULL;
+
+	sigrenderer = malloc(sizeof(*sigrenderer));
+	if (!sigrenderer)
+		return NULL;
+
+	sigrenderer->desc = signal->desc;
+
+	proc = sigrenderer->desc->start_sigrenderer;
+
+	if (proc) {
+		duh->signal[sig] = NULL;
+		sigrenderer->sigrenderer = (*proc)(duh, signal->sigdata, n_channels, pos);
+		duh->signal[sig] = signal;
+
+		if (!sigrenderer->sigrenderer) {
+			free(sigrenderer);
+			return NULL;
+		}
+	} else
+		sigrenderer->sigrenderer = NULL;
+
+	sigrenderer->n_channels = n_channels;
+
+	sigrenderer->pos = pos;
+	sigrenderer->subpos = 0;
+
+	sigrenderer->callback = NULL;
+
+	return sigrenderer;
+}
+
+
+
+#include <stdio.h>
+void duh_sigrenderer_set_callback(
+	DUH_SIGRENDERER *sigrenderer,
+	DUH_SIGRENDERER_CALLBACK callback, void *data
+)
+{
+	(void)sigrenderer;
+	(void)callback;
+	(void)data;
+	/*fprintf(stderr,
+		"Call to deprecated function duh_sigrenderer_set_callback(). The callback\n"
+		"was not installed. See dumb/docs/deprec.txt for how to fix this.\n");*/
+}
+
+
+
+void duh_sigrenderer_set_analyser_callback(
+	DUH_SIGRENDERER *sigrenderer,
+	DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data
+)
+{
+	if (sigrenderer) {
+		sigrenderer->callback = callback;
+		sigrenderer->callback_data = data;
+	}
+}
+
+
+
+int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer)
+{
+	return sigrenderer ? sigrenderer->n_channels : 0;
+}
+
+
+
+long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer)
+{
+	return sigrenderer ? sigrenderer->pos : -1;
+}
+
+
+
+void duh_sigrenderer_set_sigparam(
+	DUH_SIGRENDERER *sigrenderer,
+	unsigned char id, long value
+)
+{
+	DUH_SIGRENDERER_SET_SIGPARAM proc;
+
+	if (!sigrenderer) return;
+
+	proc = sigrenderer->desc->sigrenderer_set_sigparam;
+	if (proc)
+		(*proc)(sigrenderer->sigrenderer, id, value);
+	else
+		TRACE("Parameter #%d = %ld for signal %c%c%c%c, which does not take parameters.\n",
+			(int)id,
+			value,
+			(int)(sigrenderer->desc->type >> 24),
+			(int)(sigrenderer->desc->type >> 16),
+			(int)(sigrenderer->desc->type >> 8),
+			(int)(sigrenderer->desc->type));
+}
+
+
+
+long duh_sigrenderer_get_samples(
+	DUH_SIGRENDERER *sigrenderer,
+	float volume, float delta,
+	long size, sample_t **samples
+)
+{
+	long rendered;
+	LONG_LONG t;
+
+	if (!sigrenderer) return 0;
+
+	rendered = (*sigrenderer->desc->sigrenderer_get_samples)
+				(sigrenderer->sigrenderer, volume, delta, size, samples);
+
+	if (rendered) {
+		if (sigrenderer->callback)
+			(*sigrenderer->callback)(sigrenderer->callback_data,
+				(const sample_t *const *)samples, sigrenderer->n_channels, rendered);
+
+		t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered;
+
+		sigrenderer->pos += (long)(t >> 16);
+		sigrenderer->subpos = (int)t & 65535;
+	}
+
+	return rendered;
+}
+
+
+
+/* DEPRECATED */
+long duh_render_signal(
+	DUH_SIGRENDERER *sigrenderer,
+	float volume, float delta,
+	long size, sample_t **samples
+)
+{
+	sample_t **s = create_sample_buffer(sigrenderer->n_channels, size);
+	long rendered;
+	long i;
+	int j;
+	if (!s) return 0;
+	rendered = duh_sigrenderer_get_samples(sigrenderer, volume, delta, size, s);
+	for (j = 0; j < sigrenderer->n_channels; j++)
+		for (i = 0; i < rendered; i++)
+			samples[j][i] += s[j][i] >> 8;
+	destroy_sample_buffer(s);
+	return rendered;
+}
+
+
+
+void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples)
+{
+	if (sigrenderer)
+		(*sigrenderer->desc->sigrenderer_get_current_sample)(sigrenderer->sigrenderer, volume, samples);
+}
+
+
+
+void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer)
+{
+	if (sigrenderer) {
+		if (sigrenderer->desc->end_sigrenderer)
+			if (sigrenderer->sigrenderer)
+				(*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);
+
+		free(sigrenderer);
+	}
+}
+
+
+
+DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos)
+{
+	DUH_SIGRENDERER *sigrenderer;
+
+	if (desc->start_sigrenderer && !vsigrenderer) return NULL;
+
+	sigrenderer = malloc(sizeof(*sigrenderer));
+	if (!sigrenderer) {
+		if (desc->end_sigrenderer)
+			if (vsigrenderer)
+				(*desc->end_sigrenderer)(vsigrenderer);
+		return NULL;
+	}
+
+	sigrenderer->desc = desc;
+	sigrenderer->sigrenderer = vsigrenderer;
+
+	sigrenderer->n_channels = n_channels;
+
+	sigrenderer->pos = pos;
+	sigrenderer->subpos = 0;
+
+	sigrenderer->callback = NULL;
+
+	return sigrenderer;
+}
+
+
+
+sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
+{
+	if (sigrenderer && sigrenderer->desc->type == type)
+		return sigrenderer->sigrenderer;
+
+	return NULL;
+}
+
+
+
+#if 0
+// This function is disabled because we don't know whether we want to destroy
+// the sigrenderer if the type doesn't match. We don't even know if we need
+// the function at all. Who would want to keep an IT_SIGRENDERER (for
+// instance) without keeping the DUH_SIGRENDERER?
+sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
+{
+	if (sigrenderer && sigrenderer->desc->type == type) {
+
+
+
+	if (sigrenderer) {
+		if (sigrenderer->desc->end_sigrenderer)
+			if (sigrenderer->sigrenderer)
+				(*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);
+
+		free(sigrenderer);
+	}
+
+
+
+
+
+
+		return sigrenderer->sigrenderer;
+	}
+
+	return NULL;
+}
+#endif
--- /dev/null
+++ b/dumb/src/core/unload.c
@@ -1,0 +1,64 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * unload.c - Code to free a DUH from memory.         / / \  \
+ *                                                   | <  /   \_
+ * By entheh.                                        |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+
+
+
+static void destroy_signal(DUH_SIGNAL *signal)
+{
+	if (signal) {
+		if (signal->desc)
+			if (signal->desc->unload_sigdata)
+				if (signal->sigdata)
+					(*signal->desc->unload_sigdata)(signal->sigdata);
+
+		free(signal);
+	}
+}
+
+
+
+/* unload_duh(): destroys a DUH struct. You must call this for every DUH
+ * struct created, when you've finished with it.
+ */
+void unload_duh(DUH *duh)
+{
+	int i;
+
+	if (duh) {
+		if (duh->signal) {
+			for (i = 0; i < duh->n_signals; i++)
+				destroy_signal(duh->signal[i]);
+
+			free(duh->signal);
+		}
+
+		if (duh->tag) {
+			if (duh->tag[0][0])
+				free(duh->tag[0][0]);
+			free(duh->tag);
+		}
+
+		free(duh);
+	}
+}
--- /dev/null
+++ b/dumb/src/helpers/barray.c
@@ -1,0 +1,159 @@
+#include "internal/barray.h"
+
+#include <string.h>
+
+
+void * bit_array_create(size_t size)
+{
+	size_t bsize = ((size + 7) >> 3) + sizeof(size_t);
+	void * ret = calloc(1, bsize);
+	if (ret) *(size_t *)ret = size;
+	return ret;
+}
+
+void bit_array_destroy(void * array)
+{
+	if (array) free(array);
+}
+
+void * bit_array_dup(void * array)
+{
+	if (array)
+	{
+		size_t * size = (size_t *) array;
+		size_t bsize = ((*size + 7) >> 3) + sizeof(*size);
+		void * ret = malloc(bsize);
+		if (ret) memcpy(ret, array, bsize);
+		return ret;
+	}
+	return NULL;
+}
+
+void bit_array_reset(void * array)
+{
+	if (array)
+	{
+		size_t * size = (size_t *) array;
+		size_t bsize = (*size + 7) >> 3;
+		memset(size + 1, 0, bsize);
+	}
+}
+
+
+void bit_array_set(void * array, size_t bit)
+{
+	if (array)
+	{
+		size_t * size = (size_t *) array;
+		if (bit < *size)
+		{
+			unsigned char * ptr = (unsigned char *)(size + 1);
+			ptr[bit >> 3] |= (1U << (bit & 7));
+		}
+	}
+}
+
+int bit_array_test(void * array, size_t bit)
+{
+	if (array)
+	{
+		size_t * size = (size_t *) array;
+		if (bit < *size)
+		{
+			unsigned char * ptr = (unsigned char *)(size + 1);
+			if (ptr[bit >> 3] & (1U << (bit & 7)))
+			{
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+int bit_array_test_range(void * array, size_t bit, size_t count)
+{
+	if (array)
+	{
+		size_t * size = (size_t *) array;
+		if (bit < *size)
+		{
+			unsigned char * ptr = (unsigned char *)(size + 1);
+			if ((bit & 7) && (count > 8))
+			{
+				while ((bit < *size) && count && (bit & 7))
+				{
+					if (ptr[bit >> 3] & (1U << (bit & 7))) return 1;
+					bit++;
+					count--;
+				}
+			}
+			if (!(bit & 7))
+			{
+				while (((*size - bit) >= 8) && (count >= 8))
+				{
+					if (ptr[bit >> 3]) return 1;
+					bit += 8;
+					count -= 8;
+				}
+			}
+			while ((bit < *size) && count)
+			{
+				if (ptr[bit >> 3] & (1U << (bit & 7))) return 1;
+				bit++;
+				count--;
+			}
+		}
+	}
+	return 0;
+}
+
+void bit_array_clear(void * array, size_t bit)
+{
+	if (array)
+	{
+		size_t * size = (size_t *) array;
+		if (bit < *size)
+		{
+			unsigned char * ptr = (unsigned char *)(size + 1);
+			ptr[bit >> 3] &= ~(1U << (bit & 7));
+		}
+	}
+}
+
+void bit_array_merge(void * dest, void * source, size_t offset)
+{
+	if (dest && source)
+	{
+		size_t * dsize = (size_t *) dest;
+		size_t * ssize = (size_t *) source;
+		size_t soffset = 0;
+		while (offset < *dsize && soffset < *ssize)
+		{
+			if (bit_array_test(source, soffset))
+			{
+				bit_array_set(dest, offset);
+			}
+			soffset++;
+			offset++;
+		}
+	}
+}
+
+void bit_array_mask(void * dest, void * source, size_t offset)
+{
+	if (dest && source)
+	{
+		size_t * dsize = (size_t *) dest;
+		size_t * ssize = (size_t *) source;
+		size_t soffset = 0;
+		while (offset < *dsize && soffset < *ssize)
+		{
+			if (bit_array_test(source, soffset))
+			{
+				bit_array_clear(dest, offset);
+			}
+			soffset++;
+			offset++;
+		}
+	}
+}
--- /dev/null
+++ b/dumb/src/helpers/clickrem.c
@@ -1,0 +1,270 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * clickrem.c - Click removal helpers.                / / \  \
+ *                                                   | <  /   \_
+ * By entheh.                                        |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include "dumb.h"
+
+
+
+typedef struct DUMB_CLICK DUMB_CLICK;
+
+
+struct DUMB_CLICK_REMOVER
+{
+	DUMB_CLICK *click;
+	int n_clicks;
+
+	int offset;
+};
+
+
+struct DUMB_CLICK
+{
+	DUMB_CLICK *next;
+	long pos;
+	sample_t step;
+};
+
+
+
+DUMB_CLICK_REMOVER *dumb_create_click_remover(void)
+{
+	DUMB_CLICK_REMOVER *cr = malloc(sizeof(*cr));
+	if (!cr) return NULL;
+
+	cr->click = NULL;
+	cr->n_clicks = 0;
+
+	cr->offset = 0;
+
+	return cr;
+}
+
+
+
+void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step)
+{
+	DUMB_CLICK *click;
+
+	ASSERT(pos >= 0);
+
+	if (!cr || !step) return;
+
+	if (pos == 0) {
+		cr->offset -= step;
+		return;
+	}
+
+	click = malloc(sizeof(*click));
+	if (!click) return;
+
+	click->pos = pos;
+	click->step = step;
+
+	click->next = cr->click;
+	cr->click = click;
+	cr->n_clicks++;
+}
+
+
+
+static DUMB_CLICK *dumb_click_mergesort(DUMB_CLICK *click, int n_clicks)
+{
+	int i;
+	DUMB_CLICK *c1, *c2, **cp;
+
+	if (n_clicks <= 1) return click;
+
+	/* Split the list into two */
+	c1 = click;
+	cp = &c1;
+	for (i = 0; i < n_clicks; i += 2) cp = &(*cp)->next;
+	c2 = *cp;
+	*cp = NULL;
+
+	/* Sort the sublists */
+	c1 = dumb_click_mergesort(c1, (n_clicks + 1) >> 1);
+	c2 = dumb_click_mergesort(c2, n_clicks >> 1);
+
+	/* Merge them */
+	cp = &click;
+	while (c1 && c2) {
+		if (c1->pos > c2->pos) {
+			*cp = c2;
+			c2 = c2->next;
+		} else {
+			*cp = c1;
+			c1 = c1->next;
+		}
+		cp = &(*cp)->next;
+	}
+	if (c2)
+		*cp = c2;
+	else
+		*cp = c1;
+
+	return click;
+}
+
+
+
+void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, float halflife)
+{
+	DUMB_CLICK *click;
+	long pos = 0;
+	int offset;
+	int factor;
+
+	if (!cr) return;
+
+	factor = (int)floor(pow(0.5, 1.0/halflife) * (1U << 31));
+
+	click = dumb_click_mergesort(cr->click, cr->n_clicks);
+	cr->click = NULL;
+	cr->n_clicks = 0;
+
+	while (click) {
+		DUMB_CLICK *next = click->next;
+		ASSERT(click->pos <= length);
+		offset = cr->offset;
+		if (offset < 0) {
+			offset = -offset;
+			while (pos < click->pos) {
+				samples[pos++] -= offset;
+				offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32);
+			}
+			offset = -offset;
+		} else {
+			while (pos < click->pos) {
+				samples[pos++] += offset;
+				offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32);
+			}
+		}
+		cr->offset = offset - click->step;
+		free(click);
+		click = next;
+	}
+
+	offset = cr->offset;
+	if (offset < 0) {
+		offset = -offset;
+		while (pos < length) {
+			samples[pos++] -= offset;
+			offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+		}
+		offset = -offset;
+	} else {
+		while (pos < length) {
+			samples[pos++] += offset;
+			offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
+		}
+	}
+	cr->offset = offset;
+}
+
+
+
+sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr)
+{
+	return cr ? cr->offset : 0;
+}
+
+
+
+void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr)
+{
+	if (cr) {
+		DUMB_CLICK *click = cr->click;
+		while (click) {
+			DUMB_CLICK *next = click->next;
+			free(click);
+			click = next;
+		}
+		free(cr);
+	}
+}
+
+
+
+DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n)
+{
+	int i;
+	DUMB_CLICK_REMOVER **cr;
+	if (n <= 0) return NULL;
+	cr = malloc(n * sizeof(*cr));
+	if (!cr) return NULL;
+	for (i = 0; i < n; i++) cr[i] = dumb_create_click_remover();
+	return cr;
+}
+
+
+
+void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step)
+{
+	if (cr) {
+		int i;
+		for (i = 0; i < n; i++)
+			dumb_record_click(cr[i], pos, step[i]);
+	}
+}
+
+
+
+void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step)
+{
+	if (cr) {
+		int i;
+		for (i = 0; i < n; i++)
+			dumb_record_click(cr[i], pos, -step[i]);
+	}
+}
+
+
+
+void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife)
+{
+	if (cr) {
+		int i;
+		for (i = 0; i < n; i++)
+			dumb_remove_clicks(cr[i], samples[i], length, halflife);
+	}
+}
+
+
+
+void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset)
+{
+	if (cr) {
+		int i;
+		for (i = 0; i < n; i++)
+			if (cr[i]) offset[i] += cr[i]->offset;
+	}
+}
+
+
+
+void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr)
+{
+	if (cr) {
+		int i;
+		for (i = 0; i < n; i++) dumb_destroy_click_remover(cr[i]);
+		free(cr);
+	}
+}
--- /dev/null
+++ b/dumb/src/helpers/memfile.c
@@ -1,0 +1,96 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * memfile.c - Module for reading data from           / / \  \
+ *             memory using a DUMBFILE.              | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+
+
+
+typedef struct MEMFILE MEMFILE;
+
+struct MEMFILE
+{
+	const char *ptr;
+	long left;
+};
+
+
+
+static int dumb_memfile_skip(void *f, long n)
+{
+	MEMFILE *m = f;
+	if (n > m->left) return -1;
+	m->ptr += n;
+	m->left -= n;
+	return 0;
+}
+
+
+
+static int dumb_memfile_getc(void *f)
+{
+	MEMFILE *m = f;
+	if (m->left <= 0) return -1;
+	m->left--;
+	return *(const unsigned char *)m->ptr++;
+}
+
+
+
+static long dumb_memfile_getnc(char *ptr, long n, void *f)
+{
+	MEMFILE *m = f;
+	if (n > m->left) n = m->left;
+	memcpy(ptr, m->ptr, n);
+	m->ptr += n;
+	m->left -= n;
+	return n;
+}
+
+
+
+static void dumb_memfile_close(void *f)
+{
+	free(f);
+}
+
+
+
+static const DUMBFILE_SYSTEM memfile_dfs = {
+	NULL,
+	&dumb_memfile_skip,
+	&dumb_memfile_getc,
+	&dumb_memfile_getnc,
+	&dumb_memfile_close
+};
+
+
+
+DUMBFILE *dumbfile_open_memory(const char *data, long size)
+{
+	MEMFILE *m = malloc(sizeof(*m));
+	if (!m) return NULL;
+
+	m->ptr = data;
+	m->left = size;
+
+	return dumbfile_open_ex(m, &memfile_dfs);
+}
--- /dev/null
+++ b/dumb/src/helpers/resample.c
@@ -1,0 +1,943 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * resample.c - Resampling helpers.                   / / \  \
+ *                                                   | <  /   \_
+ * By Bob and entheh.                                |  \/ /\   /
+ *                                                    \_  /  > /
+ * In order to find a good trade-off between            | \ / /
+ * speed and accuracy in this code, some tests          |  ' /
+ * were carried out regarding the behaviour of           \__/
+ * long long ints with gcc. The following code
+ * was tested:
+ *
+ * int a, b, c;
+ * c = ((long long)a * b) >> 16;
+ *
+ * DJGPP GCC Version 3.0.3 generated the following assembly language code for
+ * the multiplication and scaling, leaving the 32-bit result in EAX.
+ *
+ * movl  -8(%ebp), %eax    ; read one int into EAX
+ * imull -4(%ebp)          ; multiply by the other; result goes in EDX:EAX
+ * shrdl $16, %edx, %eax   ; shift EAX right 16, shifting bits in from EDX
+ *
+ * Note that a 32*32->64 multiplication is performed, allowing for high
+ * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
+ * so it is a minor concern when four multiplications are being performed
+ * (the cubic resampler). On the Pentium MMX and earlier, it takes four or
+ * more cycles, so this method is unsuitable for use in the low-quality
+ * resamplers.
+ *
+ * Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
+ * defined in dumb.h. We may investigate later what code MSVC generates, but
+ * if it seems too slow then we suggest you use a good compiler.
+ *
+ * FIXME: these comments are somewhat out of date now.
+ */
+
+#include <math.h>
+#include "dumb.h"
+
+
+
+/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is
+ * called when it should be. There will be a considerable performance hit,
+ * since at least one condition has to be tested for every sample generated.
+ */
+#ifdef HEAVYDEBUG
+#define HEAVYASSERT(cond) ASSERT(cond)
+#else
+#define HEAVYASSERT(cond)
+#endif
+
+
+
+/* A global variable for controlling resampling quality wherever a local
+ * specification doesn't override it. The following values are valid:
+ *
+ *  0 - DUMB_RQ_ALIASING - fastest
+ *  1 - DUMB_RQ_LINEAR
+ *  2 - DUMB_RQ_CUBIC    - nicest
+ *
+ * Values outside the range 0-2 will behave the same as the nearest
+ * value within the range.
+ */
+int dumb_resampling_quality = DUMB_RQ_CUBIC;
+
+
+
+//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16))
+//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14)
+#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
+#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32))
+
+
+
+/* Executes the content 'iterator' times.
+ * Clobbers the 'iterator' variable.
+ * The loop is unrolled by four.
+ */
+#define LOOP4(iterator, CONTENT) \
+{ \
+	if ((iterator) & 2) { \
+		CONTENT; \
+		CONTENT; \
+	} \
+	if ((iterator) & 1) { \
+		CONTENT; \
+	} \
+	(iterator) >>= 2; \
+	while (iterator) { \
+		CONTENT; \
+		CONTENT; \
+		CONTENT; \
+		CONTENT; \
+		(iterator)--; \
+	} \
+}
+
+
+
+#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */
+#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */
+
+#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX)
+#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX)
+#define process_pickup PASTE(process_pickup, SUFFIX)
+#define dumb_resample PASTE(dumb_resample, SUFFIX)
+#define dumb_resample_get_current_sample PASTE(dumb_resample_get_current_sample, SUFFIX)
+#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX)
+
+#define X PASTE(x.x, SRCBITS)
+
+
+
+/* Cubic resampler: look-up tables
+ *
+ * a = 1.5*x1 - 1.5*x2 + 0.5*x3 - 0.5*x0
+ * b = 2*x2 + x0 - 2.5*x1 - 0.5*x3
+ * c = 0.5*x2 - 0.5*x0
+ * d = x1
+ *
+ * x = a*t*t*t + b*t*t + c*t + d
+ *   = (-0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3) * t*t*t +
+ *     (   1*x0 - 2.5*x1 + 2  *x2 - 0.5*x3) * t*t +
+ *     (-0.5*x0          + 0.5*x2         ) * t +
+ *     (            1*x1                  )
+ *   = (-0.5*t*t*t + 1  *t*t - 0.5*t    ) * x0 +
+ *     ( 1.5*t*t*t - 2.5*t*t         + 1) * x1 +
+ *     (-1.5*t*t*t + 2  *t*t + 0.5*t    ) * x2 +
+ *     ( 0.5*t*t*t - 0.5*t*t            ) * x3
+ *   = A0(t) * x0 + A1(t) * x1 + A2(t) * x2 + A3(t) * x3
+ *
+ * A0, A1, A2 and A3 stay within the range [-1,1].
+ * In the tables, they are scaled with 14 fractional bits.
+ *
+ * Turns out we don't need to store A2 and A3; they are symmetrical to A1 and A0.
+ *
+ * TODO: A0 and A3 stay very small indeed. Consider different scale/resolution?
+ */
+
+static short cubicA0[1025], cubicA1[1025];
+
+/*static*/ void init_cubic(void)
+{
+	unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */
+	static int done = 0;
+	if (done) return;
+	done = 1;
+	for (t = 0; t < 1025; t++) {
+		cubicA0[t] = -(  t*t*t >> 17) + (  t*t >> 6) - (t << 3);
+		cubicA1[t] =  (3*t*t*t >> 17) - (5*t*t >> 7)            + (1 << 14);
+	}
+}
+
+
+
+#define SUFFIX
+#define SRCTYPE sample_t
+#define SRCBITS 24
+#define ALIAS(x) MULSC(x, vol)
+#define LINEAR(x0, x1) MULSC(x0 + MULSC(x1 - x0, subpos), vol)
+/*
+#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
+	a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \
+	b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \
+	c = (x2 - x0) >> 1; \
+}
+#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol)
+*/
+#define CUBIC(x0, x1, x2, x3) MULSC( \
+	MULSC(x0, cubicA0[subpos >> 6] << 2) + \
+	MULSC(x1, cubicA1[subpos >> 6] << 2) + \
+	MULSC(x2, cubicA1[1 + (subpos >> 6 ^ 1023)] << 2) + \
+	MULSC(x3, cubicA0[1 + (subpos >> 6 ^ 1023)] << 2), vol)
+#include "resample.inc"
+
+#define SUFFIX _16
+#define SRCTYPE short
+#define SRCBITS 16
+#define ALIAS(x) MULSC16(x, vol)
+#define LINEAR(x0, x1) MULSC((x0 << 8) + MULSC16(x1 - x0, subpos), vol)
+/*
+#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
+	a = (3 * (x1 - x2) + (x3 - x0)) << 7; \
+	b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \
+	c = (x2 - x0) << 7; \
+}
+#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol)
+*/
+#define CUBIC(x0, x1, x2, x3) (int)((LONG_LONG)( \
+	x0 * cubicA0[subpos >> 6] + \
+	x1 * cubicA1[subpos >> 6] + \
+	x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \
+	x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) * (vol << 10) >> 32)
+#include "resample.inc"
+
+#define SUFFIX _8
+#define SRCTYPE signed char
+#define SRCBITS 8
+#define ALIAS(x) (x * vol)
+#define LINEAR(x0, x1) MULSC((x0 << 16) + (x1 - x0) * subpos, vol)
+/*
+#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
+	a = 3 * (x1 - x2) + (x3 - x0); \
+	b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \
+	c = (x2 - x0) << 15; \
+}
+#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol)
+*/
+#define CUBIC(x0, x1, x2, x3) (int)((LONG_LONG)(( \
+	x0 * cubicA0[subpos >> 6] + \
+	x1 * cubicA1[subpos >> 6] + \
+	x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \
+	x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) << 6) * (vol << 12) >> 32)
+#include "resample.inc"
+
+
+
+#undef dumb_reset_resampler
+#undef dumb_start_resampler
+#undef process_pickup
+#undef dumb_resample
+#undef dumb_resample_get_current_sample
+#undef dumb_end_resampler
+
+
+
+void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, long pos, long start, long end, int quality)
+{
+	if (n == 8)
+		dumb_reset_resampler_8(resampler, src, pos, start, end, quality);
+	else if (n == 16)
+		dumb_reset_resampler_16(resampler, src, pos, start, end, quality);
+	else
+		dumb_reset_resampler(resampler, src, pos, start, end, quality);
+}
+
+
+
+DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, long pos, long start, long end, int quality)
+{
+	if (n == 8)
+		return dumb_start_resampler_8(src, pos, start, end, quality);
+	else if (n == 16)
+		return dumb_start_resampler_16(src, pos, start, end, quality);
+	else
+		return dumb_start_resampler(src, pos, start, end, quality);
+}
+
+
+
+long dumb_resample_n(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float * volume, float volume_delta, float volume_target, float volume_mix, float delta)
+{
+	if (n == 8)
+		return dumb_resample_8(resampler, dst, dst_size, volume, volume_delta, volume_target, volume_mix, delta);
+	else if (n == 16)
+		return dumb_resample_16(resampler, dst, dst_size, volume, volume_delta, volume_target, volume_mix, delta);
+	else
+		return dumb_resample(resampler, dst, dst_size, volume, volume_delta, volume_target, volume_mix, delta);
+}
+
+
+
+sample_t dumb_resample_get_current_sample_n(int n, DUMB_RESAMPLER *resampler, float volume)
+{
+	if (n == 8)
+		return dumb_resample_get_current_sample_8(resampler, volume);
+	else if (n == 16)
+		return dumb_resample_get_current_sample_16(resampler, volume);
+	else
+		return dumb_resample_get_current_sample(resampler, volume);
+}
+
+
+
+void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler)
+{
+	if (n == 8)
+		return dumb_end_resampler_8(resampler);
+	else if (n == 16)
+		return dumb_end_resampler_16(resampler);
+	else
+		return dumb_end_resampler(resampler);
+}
+
+
+
+#if 0
+/* The following macro is used to overcome the fact that most C
+ * compilers (including gcc and MSVC) can't correctly multiply signed
+ * integers outside the range -32768 to 32767. i86 assembler versions
+ * don't need to use this method, since the processor does in fact
+ * have instructions to multiply large numbers correctly - which
+ * means using assembly language could make a significant difference
+ * to the speed.
+ *
+ * The basic method is as follows. We halve the subposition (how far
+ * we are between samples), so it never exceeds 32767. We also halve
+ * the delta, which is the amount to be added to the subposition each
+ * time. Then we unroll the loop twofold, so that we can add the lost
+ * one every other time if necessary (since the halving may have
+ * resulted in rounding down).
+ *
+ * This method doesn't incur any cumulative inaccuracies. There is a
+ * very slight loss of quality, which I challenge anyone to notice -
+ * but the position will advance at *exactly* the same rate as it
+ * would if we didn't use this method. This also means the pitch is
+ * exactly the same, which may even make a difference to trained
+ * musicians when resampling down a lot :)
+ *
+ * Each time this macro is invoked, DO_RESAMPLE(inc) must be defined
+ * to calculate the samples by the appropriate equation (linear,
+ * cubic, etc.). See the individual cases for examples of how this is
+ * done.
+ */
+#define MAKE_RESAMPLER()							\
+{													\
+	if (dt & 1) {									\
+		long todo2;									\
+													\
+		dt >>= 1;									\
+													\
+		if (src_subpos & 1) {						\
+			src_subpos >>= 1;						\
+			DO_RESAMPLE(1);							\
+			todo--;									\
+		} else										\
+			src_subpos >>= 1;						\
+													\
+		todo2 = todo >> 1;							\
+													\
+		while (todo2) {								\
+			DO_RESAMPLE(0);							\
+			DO_RESAMPLE(1);							\
+			todo2--;								\
+		}											\
+													\
+		if (todo & 1) {								\
+			DO_RESAMPLE(0);							\
+			src_subpos = (src_subpos << 1) | 1;		\
+		} else										\
+			src_subpos <<= 1;						\
+													\
+		todo = 0;									\
+		dt = (dt << 1) | 1;							\
+	} else {										\
+		long subposbit = src_subpos & 1;			\
+		dt >>= 1;									\
+		src_subpos >>= 1;							\
+													\
+		if (todo & 1) {								\
+			DO_RESAMPLE(0);							\
+		}											\
+													\
+		todo >>= 1;									\
+													\
+		while (todo) {								\
+			DO_RESAMPLE(0);							\
+			DO_RESAMPLE(0);							\
+			todo--;									\
+		}											\
+													\
+		src_subpos = (src_subpos << 1) | subposbit; \
+		dt <<= 1;									\
+	}												\
+}
+
+
+
+sample_t dumb_resample_get_current_sample(
+	sample_t *src, long *_src_pos, int *_src_subpos,
+	long src_start, long src_end,
+	float volume, int *_dir,
+	DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
+)
+{
+	long src_pos = *_src_pos;
+	int src_subpos = *_src_subpos;
+	int dir = _dir ? *_dir : 1;
+
+	sample_t value = 0;
+
+	if (dir == 0)
+		return 0;
+
+	ASSERT(dir == 1 || dir == -1);
+
+	if (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end)) {
+
+		/* If there's no pick-up function, we stop. */
+		if (!pickup) {
+			dir = 0;
+			goto end;
+		}
+
+		/* Process the pick-up. It may need invoking more than once. */
+		do {
+			dir = (*pickup)(src, &src_pos, &src_subpos, &src_start, &src_end, dir, pickup_data);
+
+			if (dir == 0)
+				goto end;
+
+			ASSERT(dir == 1 || dir == -1);
+		} while (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end));
+	}
+
+	HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end));
+
+	if (dumb_resampling_quality == 0) {
+		/* Aliasing (coarse) */
+		int volume_fact = (int)(volume * 16384.0);
+		value = (src[src_pos] * volume_fact) >> 14;
+	} else if (dumb_resampling_quality <= 2) {
+		/* Linear interpolation */
+		int volume_fact = (int)(volume * 16384.0);
+		int subpos = src_subpos >> 1;
+		value = ((src[src_pos] + ((((src[src_pos + 1] - src[src_pos]) >> 1) * subpos) >> 14)) * volume_fact) >> 14;
+	} else if (dumb_resampling_quality == 3) {
+		/* Quadratic interpolation */
+		int volume_fact = (int)(volume * 16384.0);
+		int a, b;
+		sample_t *x;
+		int subpos = src_subpos >> 1;
+		x = &src[src_pos];
+		a = ((x[0] + x[2]) >> 1) - x[1];
+		b = ((x[2] - x[0]) >> 1) - (a << 1);
+		value = (((((((a * subpos) >> 15) + b) * subpos) >> 15) + x[0]) * volume_fact) >> 14;
+	} else {
+		/* Cubic interpolation */
+		int volume_fact = (int)(volume * 16384.0);
+		int a, b, c;
+		sample_t *x;
+		int subpos = src_subpos >> 1;
+		x = &src[src_pos];
+		a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1;
+		b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1);
+		c = (x[2] - x[0]) >> 1;
+		value = (((int)(((LONG_LONG)((int)(((LONG_LONG)((int)(((LONG_LONG)a * subpos) >> 15) + b) * subpos) >> 15) + c) * subpos) >> 15) + x[1]) * volume_fact) >> 14;
+	}
+
+	end:
+
+	*_src_pos = src_pos;
+	*_src_subpos = src_subpos;
+	if (_dir) *_dir = dir;
+
+	return value;
+}
+
+
+
+long dumb_resample(
+	sample_t *src, long *_src_pos, int *_src_subpos,
+	long src_start, long src_end,
+	sample_t *dst, long dst_size,
+	float volume, float delta, int *_dir,
+	DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
+)
+{
+	int dt = (int)(delta * 65536.0 + 0.5);
+	long s = 0; /* Current position in the destination buffer */
+
+	long src_pos = *_src_pos;
+	int src_subpos = *_src_subpos;
+	int dir = _dir ? *_dir : 1;
+
+	int linear_average;
+
+	if (dir == 0)
+		return 0;
+
+	ASSERT(dir == 1 || dir == -1);
+
+	linear_average = dst && dumb_resampling_quality >= 2 && dt > 65536;
+
+	if (dir < 0) dt = -dt;
+
+	if (linear_average)
+		volume /= delta;
+
+	while (s < dst_size) {
+
+		long todo;
+
+		/* Process pick-ups first, just in case. */
+
+		if (linear_average) {
+
+			/* For linear average, the pick-up point could split a sum into
+			 * two parts. We handle this by putting the pick-up code inside
+			 * the summing loop. Note that this code is only executed when we
+			 * know that a pick-up is necessary somewhere during this sum
+			 * (although it is always executed once for the first sample).
+			 * We use a separate loop further down when we know we won't have
+			 * to do a pick-up, so the condition does not need testing inside
+			 * the loop.
+			 */
+
+			float sum;
+			long i;
+			int advance;
+			int x[3];
+
+			advance = src_subpos + dt;
+
+			/* Make these negative. Then they stay within the necessary
+			 * range for integer multiplication, -32768 to 32767 ;)
+			 */
+			x[0] = ~(src_subpos >> 1); /* = -1 - (src_subpos >> 1) */
+			x[2] = x[0] ^ 0x7FFF; /* = -32768 + (src_subpos >> 1) */
+
+			sum = (float)(-((src[src_pos] * (x+1)[dir]) >> 15));
+
+			i = src_pos + (advance >> 16);
+			src_pos += dir;
+			src_subpos = (dir >> 1) & 65535; /* changes 1,-1 to 0,65535 */
+
+			advance &= 65535;
+
+			/* i is the index of the first sample NOT to sum fully,
+			 * regardless of the direction of resampling.
+			 */
+
+			while (dir < 0 ? (i < src_start) : (i >= src_end)) {
+				if (dir < 0) {
+					while (src_pos >= src_start)
+						sum += src[src_pos--];
+				} else {
+					while (src_pos < src_end)
+						sum += src[src_pos++];
+				}
+
+				i -= src_pos;
+				/* i is now the number of samples left to sum fully, except
+				 * it's negative if we're going backwards.
+				 */
+
+				if (!pickup) {
+					dir = 0;
+					goto endsum;
+				}
+
+				dir = (*pickup)(src, &src_pos, &src_subpos, &src_start, &src_end, dir, pickup_data);
+
+				if (dir == 0)
+					goto endsum;
+
+				ASSERT(dir == 1 || dir == -1);
+
+				if ((dir ^ dt) < 0) {
+					dt = -dt;
+					advance ^= 65535;
+					i = -i;
+				}
+
+				i += src_pos;
+				/* There, i is back to normal. */
+			}
+
+			for (; src_pos != i; src_pos += dir)
+				sum += src[src_pos];
+
+			src_subpos = advance;
+
+			x[2] = src_subpos >> 1;
+			x[0] = x[2] ^ 0x7FFF; /* = 32767 - (src_subpos >> 1) */
+
+			sum += (src[src_pos] * (x+1)[dir]) >> 15;
+
+			endsum:
+
+			sum *= volume;
+			dst[s] += (int)sum;
+
+			s++;
+
+			if (dir == 0)
+				break;
+
+		} else if (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end)) {
+
+			/* If there's no pick-up function, we stop. */
+			if (!pickup) {
+				dir = 0;
+				break;
+			}
+
+			/* Process the pick-up. It may need invoking more than once. */
+			do {
+				dir = (*pickup)(src, &src_pos, &src_subpos, &src_start, &src_end, dir, pickup_data);
+
+				if (dir == 0)
+					goto end;
+
+				ASSERT(dir == 1 || dir == -1);
+			} while (dir < 0 ? (src_pos < src_start) : (src_pos >= src_end));
+
+			/* Update sign of dt to match that of dir. */
+			if ((dir ^ dt) < 0)
+				dt = -dt;
+		}
+
+		/* Work out how many contiguous samples we can now render. */
+		if (dir < 0)
+			todo = (long)((((LONG_LONG)(src_pos - src_start) << 16) + src_subpos) / -dt);
+		else
+			todo = (long)((((LONG_LONG)(src_end - src_pos) << 16) - src_subpos - 1) / dt);
+
+		/* The above equations work out how many complete dt-sized
+		 * intervals there are between the current position and the loop
+		 * point (provided there is a little fractional extra). The linear
+		 * average function needs complete intervals - but the other
+		 * resamplers only read a sample from the beginning of each interval,
+		 * so they can process one extra sample in their main loops (so we
+		 * increment todo in a moment).
+		 *
+		 * The linear average function makes up the extra sample using the
+		 * specialised pick-up code above.
+		 *
+		 * Note that our above pick-up process should have absolutely ensured
+		 * that the result of this function will be nonnegative.
+		 */
+
+		ASSERT(todo >= 0);
+
+		if (!linear_average)
+			todo++;
+
+		/* Of course we don't want to overrun the output buffer! */
+		if (todo > dst_size - s)
+			todo = dst_size - s;
+
+		if (!dst) {
+
+			LONG_LONG t = src_subpos + (LONG_LONG)dt * todo;
+			src_pos += (long)(t >> 16);
+			src_subpos = (int)t & 0xFFFFl;
+
+			s += todo;
+
+		} else if (linear_average) {
+
+			float sum;
+			long i;
+			int advance;
+			int x[3];
+
+			while (todo) {
+
+				advance = src_subpos + dt;
+
+				/* Make these negative. Then they stay within the necessary
+				 * range for integer multiplication, -32768 to 32767 ;)
+				 */
+				x[0] = ~(src_subpos >> 1); /* = -1 - (src_subpos >> 1) */
+				x[2] = x[0] ^ 0x7FFF; /* = -32768 + (src_subpos >> 1) */
+
+				sum = (float)(-((src[src_pos] * (x+1)[dir]) >> 15));
+
+				i = src_pos + (advance >> 16);
+				src_pos += dir;
+				src_subpos = (dir >> 1) & 65535; /* changes 1,-1 to 0,65535 */
+
+				advance &= 65535;
+
+				/* i is the index of the first sample NOT to sum fully,
+				 * regardless of the direction of resampling.
+				 */
+
+				HEAVYASSERT(dir < 0 ? (i >= src_start) : (i < src_end));
+
+				for (; src_pos != i; src_pos += dir)
+					sum += src[src_pos];
+
+				src_subpos = advance;
+
+				x[2] = src_subpos >> 1;
+				x[0] = x[2] ^ 0x7FFF; /* = 32767 - (src_subpos >> 1) */
+
+				sum += (src[src_pos] * (x+1)[dir]) >> 15;
+
+				sum *= volume;
+				dst[s] += (int)sum;
+
+				s++;
+				todo--;
+			}
+
+		} else if (dumb_resampling_quality == 0 || (dumb_resampling_quality == 1 && delta >= 1.0)) {
+
+			/* Aliasing (coarse) */
+			int volume_fact = (int)(volume * 16384.0);
+
+			do {
+				HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end));
+				dst[s] += ((src[src_pos] * volume_fact) >> 14);
+				src_subpos += dt;
+				src_pos += src_subpos >> 16;
+				src_subpos &= 0xFFFFl;
+				s++;
+			} while (--todo);
+
+		} else if (dumb_resampling_quality <= 2) {
+
+			/* Linear interpolation */
+			int volume_fact = (int)(volume * 16384.0);
+
+			#define DO_RESAMPLE(inc)		 \
+			{								 \
+				HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+											 \
+				dst[s] += (((src[src_pos] + ((((src[src_pos + 1] - src[src_pos]) >> 1) * src_subpos) >> 14)) * volume_fact) >> 14); \
+											 \
+				src_subpos += dt + inc;		 \
+				src_pos += src_subpos >> 15; \
+				src_subpos &= 0x7FFFl;		 \
+				s++;						 \
+			}
+
+			MAKE_RESAMPLER();
+
+			#undef DO_RESAMPLE
+
+		} else if (dumb_resampling_quality == 3) {
+
+			/* Quadratic interpolation */
+
+			int volume_fact = (int)(volume * 16384.0);
+			int a = 0, b = 0;
+			sample_t *x = NULL;
+			int last_src_pos = -1;
+
+			/* AIM: no integer multiplicands must transcend the range -32768 to 32767.
+			 * This limitation is imposed by most compilers, including gcc and MSVC.
+			 *
+			 * a = 0.5 * (s0 + s2) - s1
+			 * b = -1.5 * s0 + 2 * s1 - 0.5 * s2
+			 * c = s0
+			 *
+			 * s = (a * t + b) * t + c
+			 *
+			 * In fixed-point:
+			 *
+			 * a = ((s0 + s2) >> 1) - s1
+			 * b = ((-3 * s0 - s2) >> 1) + (s1 << 1)
+			 *
+			 * s = (((((a * t) >> 16) + b) * t) >> 16) + s0
+			 *
+			 * With t halved (since t can reach 65535):
+			 *
+			 * s = (((((a * t) >> 15) + b) * t) >> 15) + s0
+			 *
+			 * a currently reaches 65536
+			 * b currently reaches 131072
+			 *
+			 * So we must use aon2
+			 *
+			 * s = (((((aon2 * t) >> 14) + b) * t) >> 15) + s0
+			 *
+			 * ((aon2 * t) >> 14) + b is 5 times too big
+			 * so we must divide by 8
+			 *
+			 * s = (((((aon2 * t) >> 17) + bon8) * t) >> 12) + s0
+			 *
+			 * aon2 = ((s0 + s2) >> 2) - (s1 >> 1)
+			 * bon8 = ((-3 * s0 - s2) >> 4) + (s1 >> 2)
+			 * or:
+			 * bon8 = ((s2 - s0) >> 4) - (aon2 >> 1)
+			 */
+
+			/* Unh4x0r3d version:
+			#define DO_RESAMPLE(inc)						\
+			{												\
+				HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+															\
+				if (src_pos != last_src_pos) {				\
+					last_src_pos = src_pos;					\
+					x = &src[src_pos];						\
+					a = ((x[0] + x[2]) >> 2) - (x[1] >> 1); \
+					b = ((x[2] - x[0]) >> 4) - (a >> 1);	\
+				}											\
+															\
+				dst[s] += ((((((((a * src_subpos) >> 17) + b) * src_subpos) >> 12) + x[0]) * volume_fact) >> 14); \
+															\
+				src_subpos += dt + inc;						\
+				src_pos += src_subpos >> 15;				\
+				src_subpos &= 0x7FFFl;						\
+				s++;										\
+			}
+			*/
+
+			/* H4x0r3d version: */
+			#define DO_RESAMPLE(inc)						\
+			{												\
+				HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+															\
+				if (src_pos != last_src_pos) {				\
+					last_src_pos = src_pos;					\
+					x = &src[src_pos];						\
+					a = ((x[0] + x[2]) >> 1) - x[1];		\
+					b = ((x[2] - x[0]) >> 1) - (a << 1);	\
+				}											\
+															\
+				dst[s] += ((((((((a * src_subpos) >> 15) + b) * src_subpos) >> 15) + x[0]) * volume_fact) >> 14); \
+															\
+				src_subpos += dt + inc;						\
+				src_pos += src_subpos >> 15;				\
+				src_subpos &= 0x7FFFl;						\
+				s++;										\
+			}
+
+			MAKE_RESAMPLER();
+
+			#undef DO_RESAMPLE
+
+		} else {
+
+			/* Cubic interpolation */
+
+			int volume_fact = (int)(volume * 16384.0);
+			int a = 0, b = 0, c = 0;
+			sample_t *x = NULL;
+			int last_src_pos = -1;
+
+			/* AIM: never multiply integers outside the range -32768 to 32767.
+			 *
+			 * a = 1.5f * (x[1] - x[2]) + (x[3] - x[0]) * 0.5f;
+			 * b = 2.0f * x[2] + x[0] - 2.5f * x[1] - x[3] * 0.5f;
+			 * c = (x[2] - x[0]) * 0.5f;
+			 *
+			 * s = ((a * t + b) * t + c) * t + x[1];
+			 *
+			 * Fixed-point version:
+			 *
+			 * a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1;
+			 * b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1);
+			 * c = (x[2] - x[0]) >> 1;
+			 *
+			 * s = ((((((((a * t) >> 15) + b) * t) >> 15) + c) * t) >> 15) + x[1];
+			 *   (with t already halved, maximum 32767)
+			 *
+			 * a is in (((1+1)*2)+(1+1)+(1+1))/2 = 8 times the required range
+			 * b is in (1*2)+1+((5*1+1)/2) = 6 times
+			 * c is in the required range
+			 *
+			 * We must use aon8
+			 *
+			 * s = ((((((((aon8 * t) >> 12) + b) * t) >> 15) + c) * t) >> 15) + x[1];
+			 *
+			 * But ((aon8 * t) >> 12) is in 2^(15+15-12) = 2^18 = 8 times
+			 * b is in 6 times
+			 * so we divide both ((aon8 * t) >> 12) and b by 16
+			 *
+			 * s = ((((((((aon8 * t) >> 16) + bon16) * t) >> 11) + c) * t) >> 15) + x[1];
+			 *
+			 * ((... + bon16) * t) >> 11 is 16 times too big
+			 * c is in the correct range
+			 * we must divide both by 32
+			 *
+			 * s = ((((((((aon8 * t) >> 16) + bon16) * t) >> 16) + con32) * t) >> 10) + x[1];
+			 *
+			 * aon8  = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 4;
+			 * bon16 = ((x[2] << 2) + (x[0] << 1) - (5 * x[1] + x[3])) >> 5;
+			 * con32 = (x[2] - x[0]) >> 6;
+			 *
+			 * A lot of accuracy is lost here. It is quite likely that some
+			 * of the above would cancel anyway, so the scaling down wouldn't
+			 * have to be so severe. However, I'm not in the mood to work it
+			 * out now :P
+			 *
+			 * It may also be worth investigating whether doing this stuff
+			 * in floats would be faster.
+			 */
+
+			/* Unh4x0r3d version:
+			#define DO_RESAMPLE(inc)		   \
+			{								   \
+				HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+											   \
+				if (src_pos != last_src_pos) { \
+					last_src_pos = src_pos;	   \
+					x = &src[src_pos];		   \
+					a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 4; \
+					b = ((x[2] << 2) + (x[0] << 1) - (5 * x[1] + x[3])) >> 5; \
+					c = (x[2] - x[0]) >> 6;	   \
+				}							   \
+											   \
+				dst[s] += ((((((((((a * src_subpos) >> 16) + b) * src_subpos) >> 16) + c) * src_subpos) >> 10) + x[1]) * volume_fact) >> 14; \
+											   \
+				src_subpos += dt + inc;		   \
+				src_pos += src_subpos >> 15;   \
+				src_subpos &= 0x7FFFl;		   \
+				s++;						   \
+			}
+			*/
+
+			/* H4x0r3d version: */
+			#define DO_RESAMPLE(inc)		   \
+			{								   \
+				HEAVYASSERT(dir < 0 ? (src_pos >= src_start) : (src_pos < src_end)); \
+											   \
+				if (src_pos != last_src_pos) { \
+					last_src_pos = src_pos;	   \
+					x = &src[src_pos];		   \
+					a = (((x[1] - x[2]) << 1) + (x[1] - x[2]) + (x[3] - x[0])) >> 1; \
+					b = (x[2] << 1) + x[0] - ((5 * x[1] + x[3]) >> 1); \
+					c = (x[2] - x[0]) >> 1;	   \
+				}							   \
+											   \
+				dst[s] += (((int)(((LONG_LONG)((int)(((LONG_LONG)((int)(((LONG_LONG)a * src_subpos) >> 15) + b) * src_subpos) >> 15) + c) * src_subpos) >> 15) + x[1]) * volume_fact) >> 14; \
+											   \
+				src_subpos += dt + inc;		   \
+				src_pos += src_subpos >> 15;   \
+				src_subpos &= 0x7FFFl;		   \
+				s++;						   \
+			}
+
+			MAKE_RESAMPLER();
+
+			#undef DO_RESAMPLE
+
+		}
+
+	}
+
+	end:
+
+	ASSERT(s <= dst_size);
+
+	*_src_pos = src_pos;
+	*_src_subpos = src_subpos;
+	if (_dir) *_dir = dir;
+
+	return s;
+}
+#endif
--- /dev/null
+++ b/dumb/src/helpers/sampbuf.c
@@ -1,0 +1,47 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * sampbuf.c - Helper for allocating sample           / / \  \
+ *             buffers.                              | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include "dumb.h"
+
+
+
+sample_t **create_sample_buffer(int n_channels, long length)
+{
+	int i;
+	sample_t **samples = malloc(n_channels * sizeof(*samples));
+	if (!samples) return NULL;
+	samples[0] = malloc(n_channels * length * sizeof(*samples[0]));
+	if (!samples[0]) {
+		free(samples);
+		return NULL;
+	}
+	for (i = 1; i < n_channels; i++) samples[i] = samples[i-1] + length;
+	return samples;
+}
+
+
+
+void destroy_sample_buffer(sample_t **samples)
+{
+	if (samples) {
+		free(samples[0]);
+		free(samples);
+	}
+}
--- /dev/null
+++ b/dumb/src/helpers/silence.c
@@ -1,0 +1,29 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * silence.c - Silencing helper.                      / / \  \
+ *                                                   | <  /   \_
+ * By entheh.                                        |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <string.h>
+#include "dumb.h"
+
+
+
+void dumb_silence(sample_t *samples, long length)
+{
+	memset(samples, 0, length * sizeof(*samples));
+}
+
--- /dev/null
+++ b/dumb/src/helpers/stdfile.c
@@ -1,0 +1,93 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * stdfile.c - stdio file input module.               / / \  \
+ *                                                   | <  /   \_
+ * By entheh.                                        |  \/ /\   /
+ *                                                    \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdio.h>
+
+#include "dumb.h"
+
+
+
+static void *dumb_stdfile_open(const char *filename)
+{
+	return fopen(filename, "rb");
+}
+
+
+
+static int dumb_stdfile_skip(void *f, long n)
+{
+	return fseek(f, n, SEEK_CUR);
+}
+
+
+
+static int dumb_stdfile_getc(void *f)
+{
+	return fgetc(f);
+}
+
+
+
+static long dumb_stdfile_getnc(char *ptr, long n, void *f)
+{
+	return fread(ptr, 1, n, f);
+}
+
+
+
+static void dumb_stdfile_close(void *f)
+{
+	fclose(f);
+}
+
+
+
+static const DUMBFILE_SYSTEM stdfile_dfs = {
+	&dumb_stdfile_open,
+	&dumb_stdfile_skip,
+	&dumb_stdfile_getc,
+	&dumb_stdfile_getnc,
+	&dumb_stdfile_close
+};
+
+
+
+void dumb_register_stdfiles(void)
+{
+	register_dumbfile_system(&stdfile_dfs);
+}
+
+
+
+static const DUMBFILE_SYSTEM stdfile_dfs_leave_open = {
+	NULL,
+	&dumb_stdfile_skip,
+	&dumb_stdfile_getc,
+	&dumb_stdfile_getnc,
+	NULL
+};
+
+
+
+DUMBFILE *dumbfile_open_stdfile(FILE *p)
+{
+	DUMBFILE *d = dumbfile_open_ex(p, &stdfile_dfs_leave_open);
+
+	return d;
+}
--- /dev/null
+++ b/dumb/src/it/itload.c
@@ -1,0 +1,43 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * itload.c - Code to read an Impulse Tracker         / / \  \
+ *            file, opening and closing it for       | <  /   \_
+ *            you.                                   |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh. Don't worry Bob, you're credited          | \ / /
+ * in itread.c!                                         |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_it(): loads an IT file into a DUH struct, returning a pointer to
+ * the DUH struct. When you have finished with it, you must pass the pointer
+ * to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_it(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_it(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
+
--- /dev/null
+++ b/dumb/src/it/itmisc.c
@@ -1,0 +1,228 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * itmisc.c - Miscellaneous functions relating        / / \  \
+ *            to module files.                       | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh)
+{
+	return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT);
+}
+
+
+
+const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->song_message : NULL;
+}
+
+
+
+int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->n_orders : 0;
+}
+
+
+
+int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->n_samples : 0;
+}
+
+
+
+int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->n_instruments : 0;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i)
+{
+	ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples);
+	return sd->sample[i].name;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i)
+{
+	ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples);
+	return sd->sample[i].filename;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i)
+{
+	ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments);
+	return sd->instrument[i].name;
+}
+
+
+
+const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i)
+{
+	ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments);
+	return sd->instrument[i].filename;
+}
+
+
+
+int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->global_volume : 0;
+}
+
+
+
+void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv)
+{
+	if (sd) sd->global_volume = gv;
+}
+
+
+
+int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->mixing_volume : 0;
+}
+
+
+
+void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv)
+{
+	if (sd) sd->mixing_volume = mv;
+}
+
+
+
+int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->speed : 0;
+}
+
+
+
+void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed)
+{
+	if (sd) sd->speed = speed;
+}
+
+
+
+int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd)
+{
+	return sd ? sd->tempo : 0;
+}
+
+
+
+void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo)
+{
+	if (sd) sd->tempo = tempo;
+}
+
+
+
+int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel)
+{
+	ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS);
+	return sd ? sd->channel_volume[channel] : 0;
+}
+
+void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume)
+{
+	ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS);
+	if (sd) sd->channel_volume[channel] = volume;
+}
+
+
+
+int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr)
+{
+	return sr ? sr->order : -1;
+}
+
+
+
+int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr)
+{
+	return sr ? sr->row : -1;
+}
+
+
+
+int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr)
+{
+	return sr ? sr->globalvolume : 0;
+}
+
+
+
+void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv)
+{
+	if (sr) sr->globalvolume = gv;
+}
+
+
+
+int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr)
+{
+	return sr ? sr->tempo : 0;
+}
+
+
+
+void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo)
+{
+	if (sr) sr->tempo = tempo;
+}
+
+
+
+int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr)
+{
+	return sr ? sr->speed : 0;
+}
+
+
+
+void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed)
+{
+	if (sr) sr->speed = speed;
+}
+
+
+
+int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel)
+{
+	return sr ? sr->channel[channel].channelvolume : 0;
+}
+
+
+
+void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume)
+{
+	if (sr) sr->channel[channel].channelvolume = volume;
+}
--- /dev/null
+++ b/dumb/src/it/itorder.c
@@ -1,0 +1,63 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * itorder.c - Code to fix invalid patterns in        / / \  \
+ *             the pattern table.                    | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Julien Cugniere.                                \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* This function ensures that any pattern mentioned in the order table but
+ * not present in the pattern table is treated as an empty 64 rows pattern.
+ * This is done by adding such a dummy pattern at the end of the pattern
+ * table, and redirect invalid orders to it.
+ * Patterns 254 and 255 are left untouched, unless the signal is an XM.
+ */
+int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata)
+{
+	int i;
+	int found_some = 0;
+
+	int first_invalid = sigdata->n_patterns;
+	int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;
+
+	for (i = 0; i < sigdata->n_orders; i++) {
+		if (sigdata->order[i] >= first_invalid && sigdata->order[i] <= last_invalid) {
+			sigdata->order[i] = sigdata->n_patterns;
+			found_some = 1;
+		}
+	}
+
+	if (found_some) {
+		IT_PATTERN *new_pattern = realloc(sigdata->pattern, sizeof(*sigdata->pattern) * (sigdata->n_patterns + 1));
+		if (!new_pattern)
+			return -1;
+		
+		new_pattern[sigdata->n_patterns].n_rows = 64;
+		new_pattern[sigdata->n_patterns].n_entries = 0;
+		new_pattern[sigdata->n_patterns].entry = NULL;
+		sigdata->pattern = new_pattern;
+		sigdata->n_patterns++;
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/dumb/src/it/itread.c
@@ -1,0 +1,1289 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * itread.c - Code to read an Impulse Tracker         / / \  \
+ *            module from an open file.              | <  /   \_
+ *                                                   |  \/ /\   /
+ * Based on the loader from an IT player by Bob.      \_  /  > /
+ * Adapted for DUMB by entheh.                          | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>//might not be necessary later; required for memset
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+#define INVESTIGATE_OLD_INSTRUMENTS
+
+
+
+static int it_seek(DUMBFILE *f, long offset)
+{
+	long pos = dumbfile_pos(f);
+
+	if (pos > offset)
+		return -1;
+
+	if (pos < offset)
+		if (dumbfile_skip(f, offset - pos))
+			return -1;
+
+	return 0;
+}
+
+
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long dword;
+
+typedef struct readblock_crap readblock_crap;
+
+struct readblock_crap {
+	unsigned char *sourcebuf;
+	unsigned char *sourcepos;
+	unsigned char *sourceend;
+	int rembits;
+};
+
+
+static int readblock(DUMBFILE *f, readblock_crap * crap)
+{
+	long size;
+	int c;
+
+	size = dumbfile_igetw(f);
+	if (size < 0)
+		return size;
+
+	crap->sourcebuf = malloc(size);
+	if (!crap->sourcebuf)
+		return -1;
+
+	c = dumbfile_getnc((char *)crap->sourcebuf, size, f);
+	if (c < size) {
+		free(crap->sourcebuf);
+		crap->sourcebuf = NULL;
+		return -1;
+	}
+
+	crap->sourcepos = crap->sourcebuf;
+	crap->sourceend = crap->sourcebuf + size;
+	crap->rembits = 8;
+	return 0;
+}
+
+
+
+static void freeblock(readblock_crap * crap)
+{
+	free(crap->sourcebuf);
+	crap->sourcebuf = NULL;
+}
+
+
+
+static int readbits(int bitwidth, readblock_crap * crap)
+{
+	int val = 0;
+	int b = 0;
+
+	if (crap->sourcepos >= crap->sourceend) return val;
+
+	while (bitwidth > crap->rembits) {
+		val |= *crap->sourcepos++ << b;
+		if (crap->sourcepos >= crap->sourceend) return val;
+		b += crap->rembits;
+		bitwidth -= crap->rembits;
+		crap->rembits = 8;
+	}
+
+	val |= (*crap->sourcepos & ((1 << bitwidth) - 1)) << b;
+	*crap->sourcepos >>= bitwidth;
+	crap->rembits -= bitwidth;
+
+	return val;
+}
+
+
+
+/** WARNING - do we even need to pass `right`? */
+/** WARNING - why bother memsetting at all? The whole array is written... */
+// if we do memset, dumb_silence() would be neater...
+static int decompress8(DUMBFILE *f, signed char *left, signed char *right, int len, int cmwt)
+{
+	int blocklen, blockpos;
+	byte bitwidth;
+	word val;
+	char d1, d2;
+	readblock_crap crap;
+
+	memset(&crap, 0, sizeof(crap));
+
+	memset(left, 0, len * sizeof(*left));
+	if (right) {
+		memset(right, 0, len * sizeof(*right));
+		len <<= 1;
+	}
+
+	while (len > 0) {
+		//Read a block of compressed data:
+		if (readblock(f, &crap))
+			return -1;
+		//Set up a few variables
+		blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes
+		blockpos = 0;
+		bitwidth = 9;
+		d1 = d2 = 0;
+		//Start the decompression:
+		while (blockpos < blocklen) {
+			//Read a value:
+			val = (word)readbits(bitwidth, &crap);
+			//Check for bit width change:
+
+			if (bitwidth < 7) { //Method 1:
+				if (val == (1 << (bitwidth - 1))) {
+					val = (word)readbits(3, &crap) + 1;
+					bitwidth = (val < bitwidth) ? val : val + 1;
+					continue;
+				}
+			}
+			else if (bitwidth < 9) { //Method 2
+				byte border = (0xFF >> (9 - bitwidth)) - 4;
+
+				if (val > border && val <= (border + 8)) {
+					val -= border;
+					bitwidth = (val < bitwidth) ? val : val + 1;
+					continue;
+				}
+			}
+			else if (bitwidth == 9) { //Method 3
+				if (val & 0x100) {
+					bitwidth = (val + 1) & 0xFF;
+					continue;
+				}
+			}
+			else { //Illegal width, abort ?
+				freeblock(&crap);
+				return -1;
+			}
+
+			//Expand the value to signed byte:
+			{
+				char v; //The sample value:
+				if (bitwidth < 8) {
+					byte shift = 8 - bitwidth;
+					v = (val << shift);
+					v >>= shift;
+				}
+				else
+					v = (char)val;
+
+				//And integrate the sample value
+				//(It always has to end with integration doesn't it ? ;-)
+				d1 += v;
+				d2 += d1;
+			}
+
+			//Store !
+			/* Version 2.15 was an unofficial version with hacked compression
+			 * code. Yay, better compression :D
+			 */
+			if (right && (len & 1))
+				*right++ = cmwt == 0x215 ? d2 : d1;
+			else
+				*left++ = cmwt == 0x215 ? d2 : d1;
+			len--;
+			blockpos++;
+		}
+		freeblock(&crap);
+	}
+	return 0;
+}
+
+
+
+static int decompress16(DUMBFILE *f, short *left, short *right, int len, int cmwt)
+{
+	int blocklen, blockpos;
+	byte bitwidth;
+	long val;
+	short d1, d2;
+	readblock_crap crap;
+
+	memset(&crap, 0, sizeof(crap));
+
+	memset(left, 0, len * sizeof(*left));
+	if (right) {
+		memset(right, 0, len * sizeof(*right));
+		len <<= 1;
+	}
+
+	while (len > 0) {
+		//Read a block of compressed data:
+		if (readblock(f, &crap))
+			return -1;
+		//Set up a few variables
+		blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes
+		blockpos = 0;
+		bitwidth = 17;
+		d1 = d2 = 0;
+		//Start the decompression:
+		while (blockpos < blocklen) {
+			val = readbits(bitwidth, &crap);
+			//Check for bit width change:
+
+			if (bitwidth < 7) { //Method 1:
+				if (val == (1 << (bitwidth - 1))) {
+					val = readbits(4, &crap) + 1;
+					bitwidth = (val < bitwidth) ? val : val + 1;
+					continue;
+				}
+			}
+			else if (bitwidth < 17) { //Method 2
+				word border = (0xFFFF >> (17 - bitwidth)) - 8;
+
+				if (val > border && val <= (border + 16)) {
+					val -= border;
+					bitwidth = val < bitwidth ? val : val + 1;
+					continue;
+				}
+			}
+			else if (bitwidth == 17) { //Method 3
+				if (val & 0x10000) {
+					bitwidth = (val + 1) & 0xFF;
+					continue;
+				}
+			}
+			else { //Illegal width, abort ?
+				freeblock(&crap);
+				return -1;
+			}
+
+			//Expand the value to signed byte:
+			{
+				short v; //The sample value:
+				if (bitwidth < 16) {
+					byte shift = 16 - bitwidth;
+					v = (short)(val << shift);
+					v >>= shift;
+				}
+				else
+					v = (short)val;
+
+				//And integrate the sample value
+				//(It always has to end with integration doesn't it ? ;-)
+				d1 += v;
+				d2 += d1;
+			}
+
+			//Store !
+			/* Version 2.15 was an unofficial version with hacked compression
+			 * code. Yay, better compression :D
+			 */
+			if (right && (len & 1))
+				*right++ = cmwt == 0x215 ? d2 : d1;
+			else
+				*left++ = cmwt == 0x215 ? d2 : d1;
+			len--;
+			blockpos++;
+		}
+		freeblock(&crap);
+	}
+	return 0;
+}
+
+
+
+static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f)
+{
+	int n;
+
+	envelope->flags = dumbfile_getc(f);
+	envelope->n_nodes = dumbfile_getc(f);
+	envelope->loop_start = dumbfile_getc(f);
+	envelope->loop_end = dumbfile_getc(f);
+	envelope->sus_loop_start = dumbfile_getc(f);
+	envelope->sus_loop_end = dumbfile_getc(f);
+	for (n = 0; n < envelope->n_nodes; n++) {
+		envelope->node_y[n] = dumbfile_getc(f);
+		envelope->node_t[n] = dumbfile_igetw(f);
+	}
+	dumbfile_skip(f, 75 - envelope->n_nodes * 3 + 1);
+
+	if (envelope->n_nodes <= 0)
+		envelope->flags &= ~IT_ENVELOPE_ON;
+	else {
+		if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON;
+		if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP;
+	}
+
+	return dumbfile_error(f);
+}
+
+
+
+static int it_read_old_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f)
+{
+	int n;
+
+	if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
+		return -1;
+
+	dumbfile_getnc(instrument->filename, 13, f);
+	instrument->filename[13] = 0;
+
+	instrument->volume_envelope.flags = dumbfile_getc(f);
+	instrument->volume_envelope.loop_start = dumbfile_getc(f);
+	instrument->volume_envelope.loop_end = dumbfile_getc(f);
+	instrument->volume_envelope.sus_loop_start = dumbfile_getc(f);
+	instrument->volume_envelope.sus_loop_end = dumbfile_getc(f);
+
+	/* Skip two unused bytes. */
+	dumbfile_skip(f, 2);
+
+	/* In the old instrument format, fadeout ranges from 0 to 64, and is
+	 * subtracted at intervals from a value starting at 512. In the new
+	 * format, all these values are doubled. Therefore we double when loading
+	 * from the old instrument format - that way we don't have to think about
+	 * it later.
+	 */
+	instrument->fadeout = dumbfile_igetw(f) << 1;
+	instrument->new_note_action = dumbfile_getc(f);
+	instrument->dup_check_type = dumbfile_getc(f);
+	instrument->dup_check_action = DCA_NOTE_CUT; // This might be wrong!
+	/** WARNING - what is the duplicate check action for old-style instruments? */
+
+	/* Skip Tracker Version and Number of Samples. These are only used in
+	 * separate instrument files. Also skip unused byte.
+	 */
+	dumbfile_skip(f, 4);
+
+	dumbfile_getnc(instrument->name, 26, f);
+	instrument->name[26] = 0;
+
+	/* Skip unused bytes following the Instrument Name. */
+	dumbfile_skip(f, 6);
+
+	instrument->pp_separation = 0;
+	instrument->pp_centre = 60;
+	instrument->global_volume = 128;
+	/** WARNING - should global_volume be 64 or something? */
+	instrument->default_pan = 32;
+	/** WARNING - should default_pan be 128, meaning don`t use? */
+	instrument->random_volume = 0;
+	instrument->random_pan = 0;
+
+	for (n = 0; n < 120; n++) {
+		instrument->map_note[n] = dumbfile_getc(f);
+		instrument->map_sample[n] = dumbfile_getc(f);
+	}
+
+	/* Skip "Volume envelope (200 bytes)". */
+	// - need to know better what this is for though.
+	dumbfile_skip(f, 200);
+
+#ifdef INVESTIGATE_OLD_INSTRUMENTS
+	fprintf(stderr, "Inst %02d Env:", n);
+#endif
+
+	for (n = 0; n < 25; n++)
+	{
+		instrument->volume_envelope.node_t[n] = dumbfile_getc(f);
+		instrument->volume_envelope.node_y[n] = dumbfile_getc(f);
+
+#ifdef INVESTIGATE_OLD_INSTRUMENTS
+		fprintf(stderr, " %d,%d",
+				instrument->volume_envelope.node_t[n],
+				instrument->volume_envelope.node_y[n]);
+#endif
+
+		// This loop is unfinished, as we can probably escape from it before
+		// the end if we want to. Hence the otherwise useless dumbfile_skip()
+		// call below.
+	}
+	dumbfile_skip(f, 50 - (n << 1));
+	instrument->volume_envelope.n_nodes = n;
+
+#ifdef INVESTIGATE_OLD_INSTRUMENTS
+	fprintf(stderr, "\n");
+#endif
+
+	if (dumbfile_error(f))
+		return -1;
+
+	{
+		IT_ENVELOPE *envelope = &instrument->volume_envelope;
+		if (envelope->n_nodes <= 0)
+			envelope->flags &= ~IT_ENVELOPE_ON;
+		else {
+			if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON;
+			if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP;
+		}
+	}
+
+	instrument->filter_cutoff = 127;
+	instrument->filter_resonance = 0;
+
+	instrument->pan_envelope.flags = 0;
+	instrument->pitch_envelope.flags = 0;
+
+	return 0;
+}
+
+
+
+static int it_read_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f)
+{
+	int n;
+
+	if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE)
+		return -1;
+
+	dumbfile_getnc(instrument->filename, 13, f);
+	instrument->filename[13] = 0;
+
+	instrument->new_note_action = dumbfile_getc(f);
+	instrument->dup_check_type = dumbfile_getc(f);
+	instrument->dup_check_action = dumbfile_getc(f);
+	instrument->fadeout = dumbfile_igetw(f);
+	instrument->pp_separation = dumbfile_getc(f);
+	instrument->pp_centre = dumbfile_getc(f);
+	instrument->global_volume = dumbfile_getc(f);
+	instrument->default_pan = dumbfile_getc(f);
+	instrument->random_volume = dumbfile_getc(f);
+	instrument->random_pan = dumbfile_getc(f);
+
+	/* Skip Tracker Version and Number of Samples. These are only used in
+	 * separate instrument files. Also skip unused byte.
+	 */
+	dumbfile_skip(f, 4);
+
+	dumbfile_getnc(instrument->name, 26, f);
+	instrument->name[26] = 0;
+
+	instrument->filter_cutoff = dumbfile_getc(f);
+	instrument->filter_resonance = dumbfile_getc(f);
+
+	/* Skip MIDI Channel, Program and Bank. */
+	dumbfile_skip(f, 4);
+
+	for (n = 0; n < 120; n++) {
+		instrument->map_note[n] = dumbfile_getc(f);
+		instrument->map_sample[n] = dumbfile_getc(f);
+	}
+
+	if (dumbfile_error(f))
+		return -1;
+
+	if (it_read_envelope(&instrument->volume_envelope, f)) return -1;
+	if (it_read_envelope(&instrument->pan_envelope, f)) return -1;
+	if (it_read_envelope(&instrument->pitch_envelope, f)) return -1;
+
+	return 0;
+}
+
+
+
+static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f)
+{
+	if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE)
+		return -1;
+
+	dumbfile_getnc(sample->filename, 13, f);
+	sample->filename[13] = 0;
+
+	sample->global_volume = dumbfile_getc(f);
+	sample->flags = dumbfile_getc(f);
+	sample->default_volume = dumbfile_getc(f);
+
+	dumbfile_getnc(sample->name, 26, f);
+	sample->name[26] = 0;
+
+	*convert = dumbfile_getc(f);
+	sample->default_pan = dumbfile_getc(f);
+	sample->length = dumbfile_igetl(f);
+	sample->loop_start = dumbfile_igetl(f);
+	sample->loop_end = dumbfile_igetl(f);
+	sample->C5_speed = dumbfile_igetl(f);
+	sample->sus_loop_start = dumbfile_igetl(f);
+	sample->sus_loop_end = dumbfile_igetl(f);
+
+#ifdef STEREO_SAMPLES_COUNT_AS_TWO
+	if (sample->flags & IT_SAMPLE_STEREO) {
+		sample->length >>= 1;
+		sample->loop_start >>= 1;
+		sample->loop_end >>= 1;
+		sample->C5_speed >>= 1;
+		sample->sus_loop_start >>= 1;
+		sample->sus_loop_end >>= 1;
+	}
+#endif
+
+	if (sample->flags & IT_SAMPLE_EXISTS) {
+		if (sample->length <= 0)
+			sample->flags &= ~IT_SAMPLE_EXISTS;
+		else {
+			if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+				sample->flags &= ~IT_SAMPLE_LOOP;
+			else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+				sample->flags &= ~IT_SAMPLE_LOOP;
+
+			if ((unsigned int)sample->sus_loop_end > (unsigned int)sample->length)
+				sample->flags &= ~IT_SAMPLE_SUS_LOOP;
+			else if ((unsigned int)sample->sus_loop_start >= (unsigned int)sample->sus_loop_end)
+				sample->flags &= ~IT_SAMPLE_SUS_LOOP;
+
+			/* We may be able to truncate the sample to save memory. */
+			if (sample->flags & IT_SAMPLE_LOOP &&
+				*convert != 0xFF) { /* not truncating compressed samples, for now... */
+				if ((sample->flags & IT_SAMPLE_SUS_LOOP) && sample->sus_loop_end >= sample->loop_end)
+					sample->length = sample->sus_loop_end;
+				else
+					sample->length = sample->loop_end;
+			}
+		}
+	}
+
+	*offset = dumbfile_igetl(f);
+
+	sample->vibrato_speed = dumbfile_getc(f);
+	sample->vibrato_depth = dumbfile_getc(f);
+	sample->vibrato_rate = dumbfile_getc(f);
+	sample->vibrato_waveform = dumbfile_getc(f);
+	sample->max_resampling_quality = -1;
+
+	return dumbfile_error(f);
+}
+
+long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	long n, len, delta;
+	signed char * ptr, * end;
+	signed char compression_table[16];
+	if (dumbfile_getnc(compression_table, 16, f) != 16)
+		return -1;
+	ptr = (signed char *) sample->left;
+	delta = 0;
+
+	end = ptr + sample->length;
+	len = (sample->length + 1) / 2;
+	for (n = 0; n < len; n++) {
+		int b = dumbfile_getc(f);
+		if (b < 0) return -1;
+		delta += compression_table[b & 0x0F];
+		*ptr++ = delta;
+		if (ptr >= end) break;
+		delta += compression_table[b >> 4];
+		*ptr++ = delta;
+	}
+
+	return 0;
+}
+
+
+static long it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f)
+{
+	long n;
+
+	sample->left = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+	if (!sample->left)
+		return -1;
+
+	if (sample->flags & IT_SAMPLE_STEREO) {
+		sample->right = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+		if (!sample->right)
+			return -1;
+	}
+
+	if (!(sample->flags & IT_SAMPLE_16BIT) && (convert == 0xFF)) {
+		if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+			return -1;
+	} else if (sample->flags & 8) {
+		/* If the sample is packed, then we must unpack it. */
+
+		/** WARNING - unresolved business here... test with ModPlug? */
+
+		if (sample->flags & IT_SAMPLE_STEREO)
+			//exit(37);
+			return -1;
+
+/*
+//#ifndef STEREO_SAMPLES_COUNT_AS_TWO
+		ASSERT(!(sample->flags & IT_SAMPLE_STEREO));
+//#endif
+*/
+		if (sample->flags & IT_SAMPLE_16BIT)
+			decompress16(f, sample->left, sample->right, sample->length, cmwt);
+		else
+			decompress8(f, sample->left, sample->right, sample->length, cmwt);
+	} else if (sample->flags & IT_SAMPLE_STEREO) {
+		if (sample->flags & IT_SAMPLE_16BIT) {
+			if (convert & 2) {
+				for (n = 0; n < sample->length; n++) {
+					((short *)sample->left)[n] = dumbfile_mgetw(f);
+					((short *)sample->right)[n] = dumbfile_mgetw(f);
+				}
+			} else {
+				for (n = 0; n < sample->length; n++) {
+					((short *)sample->left)[n] = dumbfile_igetw(f);
+					((short *)sample->right)[n] = dumbfile_igetw(f);
+				}
+			}
+		} else {
+			for (n = 0; n < sample->length; n++) {
+				((signed char *)sample->left)[n] = dumbfile_getc(f);
+				((signed char *)sample->right)[n] = dumbfile_getc(f);
+			}
+		}
+	} else if (sample->flags & IT_SAMPLE_16BIT) {
+		if (convert & 2)
+			for (n = 0; n < sample->length; n++)
+				((short *)sample->left)[n] = dumbfile_mgetw(f);
+		else
+			for (n = 0; n < sample->length; n++)
+				((short *)sample->left)[n] = dumbfile_igetw(f);
+	} else
+		for (n = 0; n < sample->length; n++)
+			((signed char *)sample->left)[n] = dumbfile_getc(f);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	if (!(convert & 1)) {
+		/* Convert to signed. */
+		if (sample->flags & IT_SAMPLE_16BIT)
+			for (n = 0; n < sample->length; n++)
+				((short *)sample->left)[n] ^= 0x8000;
+		else
+			for (n = 0; n < sample->length; n++)
+				((signed char *)sample->left)[n] ^= 0x80;
+
+		if (sample->right) {
+			if (sample->flags & IT_SAMPLE_16BIT)
+				for (n = 0; n < sample->length; n++)
+					((short *)sample->right)[n] ^= 0x8000;
+			else
+				for (n = 0; n < sample->length; n++)
+					((signed char *)sample->right)[n] ^= 0x80;
+		}
+	}
+
+	/* NOT SUPPORTED:
+	 *
+	 * convert &  4 - Samples stored as delta values
+	 * convert & 16 - Samples stored as TX-Wave 12-bit values
+	 * convert & 32 - Left/Right/All Stereo prompt
+	 */
+
+	return 0;
+}
+
+
+
+#define DETECT_DUPLICATE_CHANNELS
+#ifdef DETECT_DUPLICATE_CHANNELS
+#include <stdio.h>
+#endif
+static int it_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer)
+{
+	unsigned char cmask[DUMB_IT_N_CHANNELS];
+	unsigned char cnote[DUMB_IT_N_CHANNELS];
+	unsigned char cinstrument[DUMB_IT_N_CHANNELS];
+	unsigned char cvolpan[DUMB_IT_N_CHANNELS];
+	unsigned char ceffect[DUMB_IT_N_CHANNELS];
+	unsigned char ceffectvalue[DUMB_IT_N_CHANNELS];
+#ifdef DETECT_DUPLICATE_CHANNELS
+	IT_ENTRY *dupentry[DUMB_IT_N_CHANNELS];
+#endif
+
+	int n_entries = 0;
+	int buflen;
+	int bufpos = 0;
+
+	IT_ENTRY *entry;
+
+	unsigned char channel;
+	unsigned char mask;
+
+	memset(cmask, 0, sizeof(cmask));
+	memset(cnote, 0, sizeof(cnote));
+	memset(cinstrument, 0, sizeof(cinstrument));
+	memset(cvolpan, 0, sizeof(cvolpan));
+	memset(ceffect, 0, sizeof(ceffect));
+	memset(ceffectvalue, 0, sizeof(ceffectvalue));
+#ifdef DETECT_DUPLICATE_CHANNELS
+	{
+		int i;
+		for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL;
+	}
+#endif
+
+	buflen = dumbfile_igetw(f);
+	pattern->n_rows = dumbfile_igetw(f);
+
+	/* Skip four unused bytes. */
+	dumbfile_skip(f, 4);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	/* Read in the pattern data. */
+	dumbfile_getnc(buffer, buflen, f);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	/* Scan the pattern data, and work out how many entries we need room for. */
+	while (bufpos < buflen) {
+		unsigned char b = buffer[bufpos++];
+
+		if (b == 0) {
+			/* End of row */
+			n_entries++;
+			continue;
+		}
+
+		channel = (b - 1) & 63;
+
+		if (b & 128)
+			cmask[channel] = mask = buffer[bufpos++];
+		else
+			mask = cmask[channel];
+
+		{
+			static const unsigned char used[16] = {0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5};
+			n_entries += (mask != 0);
+			bufpos += used[mask & 15];
+		}
+	}
+
+	pattern->n_entries = n_entries;
+
+	pattern->entry = malloc(n_entries * sizeof(*pattern->entry));
+
+	if (!pattern->entry)
+		return -1;
+
+	bufpos = 0;
+	memset(cmask, 0, sizeof(cmask));
+
+	entry = pattern->entry;
+
+	while (bufpos < buflen) {
+		unsigned char b = buffer[bufpos++];
+
+		if (b == 0) {
+			/* End of row */
+			IT_SET_END_ROW(entry);
+			entry++;
+#ifdef DETECT_DUPLICATE_CHANNELS
+			{
+				int i;
+				for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL;
+			}
+#endif
+			continue;
+		}
+
+		channel = (b - 1) & 63;
+
+		if (b & 128)
+			cmask[channel] = mask = buffer[bufpos++];
+		else
+			mask = cmask[channel];
+
+		if (mask) {
+			entry->mask = (mask & 15) | (mask >> 4);
+			entry->channel = channel;
+
+			if (mask & IT_ENTRY_NOTE)
+				cnote[channel] = entry->note = buffer[bufpos++];
+			else if (mask & (IT_ENTRY_NOTE << 4))
+				entry->note = cnote[channel];
+
+			if (mask & IT_ENTRY_INSTRUMENT)
+				cinstrument[channel] = entry->instrument = buffer[bufpos++];
+			else if (mask & (IT_ENTRY_INSTRUMENT << 4))
+				entry->instrument = cinstrument[channel];
+
+			if (mask & IT_ENTRY_VOLPAN)
+				cvolpan[channel] = entry->volpan = buffer[bufpos++];
+			else if (mask & (IT_ENTRY_VOLPAN << 4))
+				entry->volpan = cvolpan[channel];
+
+			if (mask & IT_ENTRY_EFFECT) {
+				ceffect[channel] = entry->effect = buffer[bufpos++];
+				ceffectvalue[channel] = entry->effectvalue = buffer[bufpos++];
+			} else {
+				entry->effect = ceffect[channel];
+				entry->effectvalue = ceffectvalue[channel];
+			}
+
+#ifdef DETECT_DUPLICATE_CHANNELS
+			if (dupentry[channel]) {
+				FILE *f = fopen("dupentry.txt", "a");
+				if (!f) abort();
+				fprintf(f, "Two events on channel %d:", channel);
+				fprintf(f, "  Event #1:");
+				if (dupentry[channel]->mask & IT_ENTRY_NOTE      ) fprintf(f, " %03d", dupentry[channel]->note      ); else fprintf(f, " ...");
+				if (dupentry[channel]->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", dupentry[channel]->instrument); else fprintf(f, " ...");
+				if (dupentry[channel]->mask & IT_ENTRY_VOLPAN    ) fprintf(f, " %03d", dupentry[channel]->volpan    ); else fprintf(f, " ...");
+				if (dupentry[channel]->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + dupentry[channel]->effect, dupentry[channel]->effectvalue); else fprintf(f, " ...\n");
+				fprintf(f, "  Event #2:");
+				if (entry->mask & IT_ENTRY_NOTE      ) fprintf(f, " %03d", entry->note      ); else fprintf(f, " ...");
+				if (entry->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", entry->instrument); else fprintf(f, " ...");
+				if (entry->mask & IT_ENTRY_VOLPAN    ) fprintf(f, " %03d", entry->volpan    ); else fprintf(f, " ...");
+				if (entry->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + entry->effect, entry->effectvalue); else fprintf(f, " ...\n");
+				fclose(f);
+			}
+			dupentry[channel] = entry;
+#endif
+
+			entry++;
+		}
+	}
+
+	ASSERT(entry == pattern->entry + n_entries);
+
+	return 0;
+}
+
+
+
+/* Currently we assume the sample data are stored after the sample headers in
+ * module files. This assumption may be unjustified; let me know if you have
+ * trouble.
+ */
+
+#define IT_COMPONENT_SONG_MESSAGE 1
+#define IT_COMPONENT_INSTRUMENT   2
+#define IT_COMPONENT_PATTERN      3
+#define IT_COMPONENT_SAMPLE       4
+
+typedef struct IT_COMPONENT
+{
+	unsigned char type;
+	unsigned char n;
+	long offset;
+	short sampfirst; /* component[sampfirst] = first sample data after this */
+	short sampnext; /* sampnext is used to create linked lists of sample data */
+}
+IT_COMPONENT;
+
+
+
+static int it_component_compare(const void *e1, const void *e2)
+{
+	return ((const IT_COMPONENT *)e1)->offset -
+	       ((const IT_COMPONENT *)e2)->offset;
+}
+
+
+
+static sigdata_t *it_load_sigdata(DUMBFILE *f)
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	int cwt, cmwt;
+	int special;
+	int message_length, message_offset;
+
+	IT_COMPONENT *component;
+	int n_components = 0;
+
+	unsigned char sample_convert[256];
+
+	int n;
+
+	unsigned char *buffer;
+
+	if (dumbfile_mgetl(f) != IT_SIGNATURE)
+		return NULL;
+
+	sigdata = malloc(sizeof(*sigdata));
+
+	if (!sigdata)
+		return NULL;
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	dumbfile_getnc(sigdata->name, 26, f);
+	sigdata->name[26] = 0;
+
+	/* Skip pattern row highlight info. */
+	dumbfile_skip(f, 2);
+
+	sigdata->n_orders = dumbfile_igetw(f);
+	sigdata->n_instruments = dumbfile_igetw(f);
+	sigdata->n_samples = dumbfile_igetw(f);
+	sigdata->n_patterns = dumbfile_igetw(f);
+
+	cwt = dumbfile_igetw(f);
+	cmwt = dumbfile_igetw(f);
+
+	sigdata->flags = dumbfile_igetw(f);
+	special = dumbfile_igetw(f);
+
+	sigdata->global_volume = dumbfile_getc(f);
+	sigdata->mixing_volume = dumbfile_getc(f);
+	sigdata->speed = dumbfile_getc(f);
+	if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
+	sigdata->tempo = dumbfile_getc(f);
+	sigdata->pan_separation = dumbfile_getc(f); /** WARNING: use this */
+
+	/* Skip Pitch Wheel Depth */
+	dumbfile_skip(f, 1);
+
+	message_length = dumbfile_igetw(f);
+	message_offset = dumbfile_igetl(f);
+
+	/* Skip Reserved. */
+	dumbfile_skip(f, 4);
+
+	dumbfile_getnc(sigdata->channel_pan, DUMB_IT_N_CHANNELS, f);
+	dumbfile_getnc(sigdata->channel_volume, DUMB_IT_N_CHANNELS, f);
+
+	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	sigdata->order = malloc(sigdata->n_orders);
+	if (!sigdata->order) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	if (sigdata->n_instruments) {
+		sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
+		if (!sigdata->instrument) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+	}
+
+	if (sigdata->n_samples) {
+		sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+		if (!sigdata->sample) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (n = 0; n < sigdata->n_samples; n++)
+			sigdata->sample[n].right = sigdata->sample[n].left = NULL;
+	}
+
+	if (sigdata->n_patterns) {
+		sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+		if (!sigdata->pattern) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (n = 0; n < sigdata->n_patterns; n++)
+			sigdata->pattern[n].entry = NULL;
+	}
+
+	dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+	sigdata->restart_position = 0;
+
+	component = malloc(769 * sizeof(*component));
+	if (!component) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	if (special & 1) {
+		component[n_components].type = IT_COMPONENT_SONG_MESSAGE;
+		component[n_components].offset = message_offset;
+		component[n_components].sampfirst = -1;
+		n_components++;
+	}
+
+	for (n = 0; n < sigdata->n_instruments; n++) {
+		component[n_components].type = IT_COMPONENT_INSTRUMENT;
+		component[n_components].n = n;
+		component[n_components].offset = dumbfile_igetl(f);
+		component[n_components].sampfirst = -1;
+		n_components++;
+	}
+
+	for (n = 0; n < sigdata->n_samples; n++) {
+		component[n_components].type = IT_COMPONENT_SAMPLE;
+		component[n_components].n = n;
+		component[n_components].offset = dumbfile_igetl(f);
+		component[n_components].sampfirst = -1;
+		n_components++;
+	}
+
+	for (n = 0; n < sigdata->n_patterns; n++) {
+		long offset = dumbfile_igetl(f);
+		if (offset) {
+			component[n_components].type = IT_COMPONENT_PATTERN;
+			component[n_components].n = n;
+			component[n_components].offset = offset;
+			component[n_components].sampfirst = -1;
+			n_components++;
+		} else {
+			/* Empty 64-row pattern */
+			sigdata->pattern[n].n_rows = 64;
+			sigdata->pattern[n].n_entries = 0;
+		}
+	}
+
+	if (dumbfile_error(f)) {
+		free(component);
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	/*
+	if (!(sigdata->flags & 128) != !(special & 8)) {
+		fprintf(stderr, "Flags   Bit 7 (\"Request embedded MIDI configuration\"): %s\n", sigdata->flags & 128 ? "=SET=" : "clear");
+		fprintf(stderr, "Special Bit 3     (\"MIDI configuration embedded\")    : %s\n", special        &   8 ? "=SET=" : "clear");
+		fprintf(stderr, "entheh would like to investigate this IT file.\n");
+		fprintf(stderr, "Please contact him! [email protected]\n");
+	}
+	*/
+
+	if (special & 8) {
+		/* MIDI configuration is embedded. */
+		unsigned char mididata[32];
+		int i;
+		sigdata->midi = malloc(sizeof(*sigdata->midi));
+		if (!sigdata->midi) {
+			free(component);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+			// Should we be happy with this outcome in some situations?
+		}
+		// What are we skipping?
+		i = dumbfile_igetw(f);
+		if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) {
+			free(component);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		/* Read embedded MIDI configuration */
+		// What are the first 9 commands for?
+		if (dumbfile_skip(f, 32*9)) {
+			free(component);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (i = 0; i < 16; i++) {
+			unsigned char len = 0;
+			int j, leftdigit = -1;
+			if (dumbfile_getnc(mididata, 32, f) < 32) {
+				free(component);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+			sigdata->midi->SFmacroz[i] = 0;
+			for (j = 0; j < 32; j++) {
+				if (leftdigit >= 0) {
+					if (mididata[j] == 0) {
+						sigdata->midi->SFmacro[i][len++] = leftdigit;
+						break;
+					} else if (mididata[j] == ' ')
+						sigdata->midi->SFmacro[i][len++] = leftdigit;
+					else if (mididata[j] >= '0' && mididata[j] <= '9')
+						sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0');
+					else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+						sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA);
+					leftdigit = -1;
+				} else if (mididata[j] == 0)
+					break;
+				else if (mididata[j] == 'z')
+					sigdata->midi->SFmacroz[i] |= 1 << len++;
+				else if (mididata[j] >= '0' && mididata[j] <= '9')
+					leftdigit = mididata[j] - '0';
+				else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+					leftdigit = mididata[j] - 'A' + 0xA;
+			}
+			sigdata->midi->SFmacrolen[i] = len;
+		}
+		for (i = 0; i < 128; i++) {
+			unsigned char len = 0;
+			int j, leftdigit = -1;
+			dumbfile_getnc(mididata, 32, f);
+			for (j = 0; j < 32; j++) {
+				if (leftdigit >= 0) {
+					if (mididata[j] == 0) {
+						sigdata->midi->Zmacro[i][len++] = leftdigit;
+						break;
+					} else if (mididata[j] == ' ')
+						sigdata->midi->Zmacro[i][len++] = leftdigit;
+					else if (mididata[j] >= '0' && mididata[j] <= '9')
+						sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0');
+					else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+						sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA);
+					leftdigit = -1;
+				} else if (mididata[j] == 0)
+					break;
+				else if (mididata[j] >= '0' && mididata[j] <= '9')
+					leftdigit = mididata[j] - '0';
+				else if (mididata[j] >= 'A' && mididata[j] <= 'F')
+					leftdigit = mididata[j] - 'A' + 0xA;
+			}
+			sigdata->midi->Zmacrolen[i] = len;
+		}
+	}
+
+	sigdata->flags &= IT_REAL_FLAGS;
+
+	qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
+
+	buffer = malloc(65536);
+	if (!buffer) {
+		free(component);
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (n = 0; n < n_components; n++) {
+		long offset;
+		int m;
+
+		if (it_seek(f, component[n].offset)) {
+			free(buffer);
+			free(component);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+
+		switch (component[n].type) {
+
+			case IT_COMPONENT_SONG_MESSAGE:
+				sigdata->song_message = malloc(message_length + 1);
+				if (sigdata->song_message) {
+					if (dumbfile_getnc(sigdata->song_message, message_length, f) < message_length) {
+						free(buffer);
+						free(component);
+						_dumb_it_unload_sigdata(sigdata);
+						return NULL;
+					}
+					sigdata->song_message[message_length] = 0;
+				}
+				break;
+
+			case IT_COMPONENT_INSTRUMENT:
+				if (cmwt < 0x200)
+					m = it_read_old_instrument(&sigdata->instrument[component[n].n], f);
+				else
+					m = it_read_instrument(&sigdata->instrument[component[n].n], f);
+
+				if (m) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+				break;
+
+			case IT_COMPONENT_PATTERN:
+				if (it_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+				break;
+
+			case IT_COMPONENT_SAMPLE:
+				if (it_read_sample_header(&sigdata->sample[component[n].n], &sample_convert[component[n].n], &offset, f)) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+
+				if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
+					short *sample;
+
+					for (m = n + 1; m < n_components; m++)
+						if (component[m].offset > offset)
+							break;
+					m--;
+
+					sample = &component[m].sampfirst;
+
+					while (*sample >= 0 && component[*sample].offset <= offset)
+						sample = &component[*sample].sampnext;
+
+					component[n].sampnext = *sample;
+					*sample = n;
+
+					component[n].offset = offset;
+				}
+		}
+
+		m = component[n].sampfirst;
+
+		while (m >= 0) {
+			if (it_seek(f, component[m].offset)) {
+				free(buffer);
+				free(component);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+
+			if (it_read_sample_data(cmwt, &sigdata->sample[component[m].n], sample_convert[component[m].n], f)) {
+				free(buffer);
+				free(component);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+
+			m = component[m].sampnext;
+		}
+	}
+
+	free(buffer);
+	free(component);
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	return sigdata;
+}
+
+
+
+DUH *dumb_read_it(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_load_sigdata(f);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0; /*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[1][2];
+                tag[0][0] = "TITLE";
+                tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		return make_duh(length, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/itrender.c
@@ -1,0 +1,5112 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * itrender.c - Code to render an Impulse Tracker     / / \  \
+ *              module.                              | <  /   \_
+ *                                                   |  \/ /\   /
+ * Written - painstakingly - by entheh.               \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/dumb.h"
+#include "internal/it.h"
+
+// #define BIT_ARRAY_BULLSHIT
+
+#define END_RAMPING
+#define RAMP_DOWN
+
+static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel)
+{
+	IT_PLAYING *dst;
+
+	if (!src) return NULL;
+
+	dst = malloc(sizeof(*dst));
+	if (!dst) return NULL;
+
+	dst->flags = src->flags;
+	dst->resampling_quality = src->resampling_quality;
+
+	ASSERT(src->channel);
+	dst->channel = &dstchannel[src->channel - srcchannel];
+	dst->sample = src->sample;
+	dst->instrument = src->instrument;
+	dst->env_instrument = src->env_instrument;
+
+	dst->sampnum = src->sampnum;
+	dst->instnum = src->instnum;
+
+#ifdef END_RAMPING
+	dst->declick_stage = src->declick_stage;
+	dst->declick_volume = src->declick_volume;
+#endif
+
+	dst->float_volume[0] = src->float_volume[0];
+	dst->float_volume[1] = src->float_volume[1];
+
+	dst->ramp_volume[0] = src->ramp_volume[0];
+	dst->ramp_volume[1] = src->ramp_volume[1];
+
+	dst->ramp_delta[0] = src->ramp_delta[0];
+	dst->ramp_delta[1] = src->ramp_delta[1];
+
+	dst->channel_volume = src->channel_volume;
+
+	dst->volume = src->volume;
+	dst->pan = src->pan;
+
+	dst->volume_offset = src->volume_offset;
+	dst->panning_offset = src->panning_offset;
+
+	dst->note = src->note;
+
+	dst->enabled_envelopes = src->enabled_envelopes;
+
+	dst->filter_cutoff = src->filter_cutoff;
+	dst->filter_resonance = src->filter_resonance;
+
+	dst->true_filter_cutoff = src->true_filter_cutoff;
+	dst->true_filter_resonance = src->true_filter_resonance;
+
+	dst->vibrato_speed = src->vibrato_speed;
+	dst->vibrato_depth = src->vibrato_depth;
+	dst->vibrato_n = src->vibrato_n;
+	dst->vibrato_time = src->vibrato_time;
+	dst->vibrato_waveform = src->vibrato_waveform;
+
+	dst->tremolo_speed = src->tremolo_speed;
+	dst->tremolo_depth = src->tremolo_depth;
+	dst->tremolo_time = src->tremolo_time;
+	dst->tremolo_waveform = src->tremolo_waveform;
+
+	dst->panbrello_speed = src->panbrello_speed;
+	dst->panbrello_depth = src->panbrello_depth;
+	dst->panbrello_time = src->panbrello_time;
+	dst->panbrello_waveform = src->panbrello_waveform;
+	dst->panbrello_random = src->panbrello_random;
+
+	dst->sample_vibrato_time = src->sample_vibrato_time;
+	dst->sample_vibrato_waveform = src->sample_vibrato_waveform;
+	dst->sample_vibrato_depth = src->sample_vibrato_depth;
+
+	dst->slide = src->slide;
+	dst->delta = src->delta;
+	dst->finetune = src->finetune;
+
+	dst->volume_envelope = src->volume_envelope;
+	dst->pan_envelope = src->pan_envelope;
+	dst->pitch_envelope = src->pitch_envelope;
+
+	dst->fadeoutcount = src->fadeoutcount;
+
+	dst->filter_state[0] = src->filter_state[0];
+	dst->filter_state[1] = src->filter_state[1];
+
+	dst->resampler[0] = src->resampler[0];
+	dst->resampler[1] = src->resampler[1];
+	dst->resampler[1].pickup_data = dst->resampler[0].pickup_data = dst;
+	dst->time_lost = src->time_lost;
+
+	return dst;
+}
+
+
+
+static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src)
+{
+	dst->flags = src->flags;
+
+	dst->volume = src->volume;
+	dst->volslide = src->volslide;
+	dst->xm_volslide = src->xm_volslide;
+	dst->panslide = src->panslide;
+
+	dst->pan = src->pan;
+	dst->truepan = src->truepan;
+
+	dst->channelvolume = src->channelvolume;
+	dst->channelvolslide = src->channelvolslide;
+
+	dst->instrument = src->instrument;
+	dst->note = src->note;
+
+	dst->SFmacro = src->SFmacro;
+
+	dst->filter_cutoff = src->filter_cutoff;
+	dst->filter_resonance = src->filter_resonance;
+
+	dst->key_off_count = src->key_off_count;
+	dst->note_cut_count = src->note_cut_count;
+	dst->note_delay_count = src->note_delay_count;
+	dst->note_delay_entry = src->note_delay_entry;
+
+	dst->new_note_action = src->new_note_action;
+
+	dst->arpeggio = src->arpeggio;
+	dst->retrig = src->retrig;
+	dst->xm_retrig = src->xm_retrig;
+	dst->retrig_tick = src->retrig_tick;
+
+	dst->tremor_time = src->tremor_time;
+
+	dst->vibrato_waveform = src->vibrato_waveform;
+	dst->tremolo_waveform = src->tremolo_waveform;
+	dst->panbrello_waveform = src->panbrello_waveform;
+
+	dst->portamento = src->portamento;
+	dst->toneporta = src->toneporta;
+	dst->toneslide = src->toneslide;
+	dst->toneslide_tick = src->toneslide_tick;
+	dst->last_toneslide_tick = src->last_toneslide_tick;
+	dst->ptm_toneslide = src->ptm_toneslide;
+	dst->ptm_last_toneslide = src->ptm_last_toneslide;
+	dst->destnote = src->destnote;
+
+	dst->glissando = src->glissando;
+
+	dst->sample = src->sample;
+	dst->truenote = src->truenote;
+
+	dst->midi_state = src->midi_state;
+
+	dst->lastvolslide = src->lastvolslide;
+	dst->lastDKL = src->lastDKL;
+	dst->lastEF = src->lastEF;
+	dst->lastG = src->lastG;
+	dst->lastHspeed = src->lastHspeed;
+	dst->lastHdepth = src->lastHdepth;
+	dst->lastRspeed = src->lastRspeed;
+	dst->lastRdepth = src->lastRdepth;
+	dst->lastYspeed = src->lastYspeed;
+	dst->lastYdepth = src->lastYdepth;
+	dst->lastI = src->lastI;
+	dst->lastJ = src->lastJ;
+	dst->lastN = src->lastN;
+	dst->lastO = src->lastO;
+	dst->high_offset = src->high_offset;
+	dst->lastP = src->lastP;
+	dst->lastQ = src->lastQ;
+	dst->lastS = src->lastS;
+	dst->pat_loop_row = src->pat_loop_row;
+	dst->pat_loop_count = src->pat_loop_count;
+	dst->lastW = src->lastW;
+
+	dst->xm_lastE1 = src->xm_lastE1;
+	dst->xm_lastE2 = src->xm_lastE2;
+	dst->xm_lastEA = src->xm_lastEA;
+	dst->xm_lastEB = src->xm_lastEB;
+	dst->xm_lastX1 = src->xm_lastX1;
+	dst->xm_lastX2 = src->xm_lastX2;
+
+	dst->playing = dup_playing(src->playing, dst, src);
+
+
+#ifdef BIT_ARRAY_BULLSHIT
+	dst->played_patjump = bit_array_dup(src->played_patjump);
+	dst->played_patjump_order = src->played_patjump_order;
+#endif
+}
+
+
+
+/* Allocate the new callbacks first, then pass them to this function!
+ * It will free them on failure.
+ */
+static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_channels, IT_CALLBACKS *callbacks)
+{
+	DUMB_IT_SIGRENDERER *dst;
+	int i;
+
+	if (!src) {
+		if (callbacks) free(callbacks);
+		return NULL;
+	}
+
+	dst = malloc(sizeof(*dst));
+	if (!dst) {
+		if (callbacks) free(callbacks);
+		return NULL;
+	}
+
+	dst->sigdata = src->sigdata;
+
+	dst->n_channels = n_channels;
+
+	dst->resampling_quality = src->resampling_quality;
+
+	dst->globalvolume = src->globalvolume;
+	dst->globalvolslide = src->globalvolslide;
+
+	dst->tempo = src->tempo;
+	dst->temposlide = src->temposlide;
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++)
+		dup_channel(&dst->channel[i], &src->channel[i]);
+
+	for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+		dst->playing[i] = dup_playing(src->playing[i], dst->channel, src->channel);
+
+	dst->tick = src->tick;
+	dst->speed = src->speed;
+	dst->rowcount = src->rowcount;
+
+	dst->order = src->order;
+	dst->row = src->row;
+	dst->processorder = src->processorder;
+	dst->processrow = src->processrow;
+	dst->breakrow = src->breakrow;
+
+	dst->restart_position = src->restart_position;
+
+	dst->n_rows = src->n_rows;
+
+	dst->entry_start = src->entry_start;
+	dst->entry = src->entry;
+	dst->entry_end = src->entry_end;
+
+	dst->time_left = src->time_left;
+	dst->sub_time_left = src->sub_time_left;
+
+	dst->click_remover = NULL;
+
+	dst->callbacks = callbacks;
+
+#ifdef BIT_ARRAY_BULLSHIT
+	dst->played = bit_array_dup(src->played);
+#endif
+
+	dst->gvz_time = src->gvz_time;
+	dst->gvz_sub_time = src->gvz_sub_time;
+
+	return dst;
+}
+
+
+
+static const IT_MIDI default_midi = {
+	/* unsigned char SFmacro[16][16]; */
+	{
+		{0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+	},
+	/* unsigned char SFmacrolen[16]; */
+	{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+	/* unsigned short SFmacroz[16]; */
+	/* Bitfield; bit 0 set = z in first position */
+	{
+		0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+	},
+	/* unsigned char Zmacro[128][16]; */
+	{
+		{0xF0, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0xF0, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+		{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+	},
+	/* unsigned char Zmacrolen[128]; */
+	{
+		4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+	}
+};
+
+
+
+static void it_reset_filter_state(IT_FILTER_STATE *state)
+{
+	state->currsample = 0;
+	state->prevsample = 0;
+}
+
+
+
+#define LOG10 2.30258509299
+
+/* IMPORTANT: This function expects one extra sample in 'src' so it can apply
+ * click removal. It reads size samples, starting from src[0], and writes its
+ * output starting at dst[pos]. The pos parameter is required for getting
+ * click removal right.
+ */
+static void it_filter(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int sampfreq, int cutoff, int resonance)
+{
+	float currsample = state->currsample;
+	float prevsample = state->prevsample;
+
+	float a, b, c;
+
+	{
+		float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24<<IT_ENVELOPE_SHIFT))) * (1.0/(2*3.14159265358979323846*110.0)));
+		float loss = (float)exp(resonance*(-LOG10*1.2/128.0));
+		float d, e;
+#if 0
+		loss *= 2; // This is the mistake most players seem to make!
+#endif
+
+#if 1
+		d = (1.0f - loss) / inv_angle;
+		if (d > 2.0f) d = 2.0f;
+		d = (loss - d) * inv_angle;
+		e = inv_angle * inv_angle;
+		a = 1.0f / (1.0f + d + e);
+		c = -e * a;
+		b = 1.0f - a - c;
+#else
+		a = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss);
+		c = -(inv_angle*inv_angle) * a;
+		b = 1.0f - a - c;
+#endif
+	}
+
+	dst += pos;
+
+	if (cr) {
+		float startstep = src[0]*a + currsample*b + prevsample*c;
+		dumb_record_click(cr, pos, (sample_t)startstep);
+	}
+
+#define INT_FILTERS
+#ifdef INT_FILTERS
+#define MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32))
+#define SCALEB 12
+	{
+		int ai = (int)(a * (1 << (16+SCALEB)));
+		int bi = (int)(b * (1 << (16+SCALEB)));
+		int ci = (int)(c * (1 << (16+SCALEB)));
+		sample_t csi = (sample_t)currsample;
+		sample_t psi = (sample_t)prevsample;
+		sample_t *dst_end = dst + size;
+		while (dst < dst_end) {
+			{
+				sample_t nsi = MULSCA(*src++, ai) + MULSCA(csi, bi) + MULSCA(psi, ci);
+				psi = csi;
+				csi = nsi;
+			}
+			*dst++ += csi;
+		}
+		currsample = csi;
+		prevsample = psi;
+	}
+#else
+	{
+		int i = size % 3;
+		while (i > 0) {
+			{
+				float newsample = *src++*a + currsample*b + prevsample*c;
+				prevsample = currsample;
+				currsample = newsample;
+			}
+			*dst++ += (sample_t)currsample;
+			i--;
+		}
+		i = size / 3;
+		while (i > 0) {
+			float newsample;
+			/* Gotta love unrolled loops! */
+			*dst++ += (sample_t)(newsample = *src++*a + currsample*b + prevsample*c);
+			*dst++ += (sample_t)(prevsample = *src++*a + newsample*b + currsample*c);
+			*dst++ += (sample_t)(currsample = *src++*a + prevsample*b + newsample*c);
+			i--;
+		}
+	}
+#endif
+
+	if (cr) {
+		float endstep = *src*a + currsample*b + prevsample*c;
+		dumb_record_click(cr, pos + size, -(sample_t)endstep);
+	}
+
+	state->currsample = currsample;
+	state->prevsample = prevsample;
+}
+
+#undef LOG10
+
+
+
+static const signed char it_sine[256] = {
+	  0,  2,  3,  5,  6,  8,  9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+	 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+	 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+	 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+	 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+	 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+	 24, 23, 22, 20, 19, 17, 16, 14, 12, 11,  9,  8,  6,  5,  3,  2,
+	  0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+	-24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+	-45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+	-59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+	-59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+	-45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+	-24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2
+};
+
+
+
+#if 1
+/** WARNING: use these! */
+/** JULIEN: Plus for XM compatibility it could be interesting to rename
+ * it_sawtooth[] to it_rampdown[], and add an it_rampup[].
+ * Also, still for XM compat', twood be good if it was possible to tell the
+ * the player not to retrig' the waveform on a new instrument.
+ * Both of these are only for completness though, as I don't think it would
+ * be very noticeable ;)
+ */
+/** ENTHEH: IT also has the 'don't retrig' thingy :) */
+static const signed char it_sawtooth[256] = {
+	 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
+	 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48,
+	 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40,
+	 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32,
+	 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24,
+	 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+	 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10,  9,  9,  8,
+	  8,  7,  7,  6,  6,  5,  5,  4,  4,  3,  3,  2,  2,  1,  1,  0,
+	  0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8,
+	 -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16,
+	-16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,
+	-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,
+	-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,
+	-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,
+	-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56,
+	-56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64
+};
+
+static const signed char it_squarewave[256] = {
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
+};
+
+static const signed char it_xm_ramp[256] = {
+	  0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8,
+	 -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16,
+	-16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24,
+	-24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32,
+	-32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40,
+	-40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48,
+	-48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56,
+	-56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64,
+	 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56,
+	 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48,
+	 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40,
+	 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32,
+	 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24,
+	 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+	 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10,  9,  9,  8,
+	  8,  7,  7,  6,  6,  5,  5,  4,  4,  3,  3,  2,  2,  1,  1,  0
+};
+
+static const signed char it_xm_squarewave[256] = {
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,
+	-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64
+};
+
+#endif
+
+
+
+static void reset_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	int i;
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+		channel->key_off_count = 0;
+		channel->note_cut_count = 0;
+		channel->note_delay_count = 0;
+	}
+}
+
+
+
+static void reset_effects(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	int i;
+
+	sigrenderer->globalvolslide = 0;
+	sigrenderer->temposlide = 0;
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+		channel->volslide = 0;
+		channel->xm_volslide = 0;
+		channel->panslide = 0;
+		channel->channelvolslide = 0;
+		channel->arpeggio = 0;
+		channel->retrig = 0;
+		if (channel->xm_retrig) {
+			channel->xm_retrig = 0;
+			channel->retrig_tick = 0;
+		}
+		channel->tremor_time &= 127;
+		channel->portamento = 0;
+		channel->toneporta = 0;
+		if (channel->ptm_toneslide) {
+			channel->ptm_last_toneslide = channel->ptm_toneslide;
+			channel->last_toneslide_tick = channel->toneslide_tick;
+		} else
+			channel->ptm_last_toneslide = 0;
+		channel->ptm_toneslide = 0;
+		channel->toneslide_tick = 0;
+		if (channel->playing) {
+			channel->playing->vibrato_n = 0;
+			channel->playing->tremolo_speed = 0;
+			channel->playing->tremolo_depth = 0;
+			channel->playing->panbrello_speed = 0;
+		}
+	}
+}
+
+
+
+static void update_tremor(IT_CHANNEL *channel)
+{
+	if ((channel->tremor_time & 128) && channel->playing) {
+		if (channel->tremor_time == 128)
+			channel->tremor_time = (channel->lastI >> 4) | 192;
+		else if (channel->tremor_time == 192)
+			channel->tremor_time = (channel->lastI & 15) | 128;
+		else
+			channel->tremor_time--;
+	}
+}
+
+
+
+static void it_pickup_loop(DUMB_RESAMPLER *resampler, void *data)
+{
+	resampler->pos -= resampler->end - resampler->start;
+	((IT_PLAYING *)data)->time_lost += resampler->end - resampler->start;
+}
+
+
+
+static void it_pickup_pingpong_loop(DUMB_RESAMPLER *resampler, void *data)
+{
+	if (resampler->dir < 0) {
+		resampler->pos = (resampler->start << 1) - 1 - resampler->pos;
+		resampler->subpos ^= 65535;
+		resampler->dir = 1;
+		((IT_PLAYING *)data)->time_lost += (resampler->end - resampler->start) << 1;
+	} else {
+		resampler->pos = (resampler->end << 1) - 1 - resampler->pos;
+		resampler->subpos ^= 65535;
+		resampler->dir = -1;
+	}
+}
+
+
+
+static void it_pickup_stop_at_end(DUMB_RESAMPLER *resampler, void *data)
+{
+	(void)data;
+
+	if (resampler->dir < 0) {
+		resampler->pos = (resampler->start << 1) - 1 - resampler->pos;
+		resampler->subpos ^= 65535;
+		/* By rights, time_lost would be updated here. However, there is no
+		 * need at this point; it will not be used.
+		 *
+		 * ((IT_PLAYING *)data)->time_lost += (resampler->src_end - resampler->src_start) << 1;
+		 */
+		resampler->dir = 1;
+	} else
+		resampler->dir = 0;
+}
+
+
+
+static void it_playing_update_resamplers(IT_PLAYING *playing)
+{
+	if ((playing->sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) {
+		playing->resampler[0].start = playing->sample->sus_loop_start;
+		playing->resampler[0].end = playing->sample->sus_loop_end;
+		if (playing->sample->flags & IT_SAMPLE_PINGPONG_SUS_LOOP)
+			playing->resampler[0].pickup = &it_pickup_pingpong_loop;
+		else
+			playing->resampler[0].pickup = &it_pickup_loop;
+	} else if (playing->sample->flags & IT_SAMPLE_LOOP) {
+		playing->resampler[0].start = playing->sample->loop_start;
+		playing->resampler[0].end = playing->sample->loop_end;
+		if (playing->sample->flags & IT_SAMPLE_PINGPONG_LOOP)
+			playing->resampler[0].pickup = &it_pickup_pingpong_loop;
+		else
+			playing->resampler[0].pickup = &it_pickup_loop;
+	} else {
+		if (playing->sample->flags & IT_SAMPLE_SUS_LOOP)
+			playing->resampler[0].start = playing->sample->sus_loop_start;
+		else
+			playing->resampler[0].start = 0;
+		playing->resampler[0].end = playing->sample->length;
+		playing->resampler[0].pickup = &it_pickup_stop_at_end;
+	}
+	playing->resampler[1].start = playing->resampler[0].start;
+	playing->resampler[1].end = playing->resampler[0].end;
+	playing->resampler[1].pickup = playing->resampler[0].pickup;
+	ASSERT(playing->resampler[0].pickup_data == playing);
+	ASSERT(playing->resampler[1].pickup_data == playing);
+}
+
+
+
+/* This should be called whenever the sample or sample position changes. */
+static void it_playing_reset_resamplers(IT_PLAYING *playing, long pos)
+{
+	int bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8;
+	int quality = playing->resampling_quality;
+	if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+		quality = playing->sample->max_resampling_quality;
+	dumb_reset_resampler_n(bits, &playing->resampler[0], playing->sample->left, pos, 0, 0, quality);
+	dumb_reset_resampler_n(bits, &playing->resampler[1], playing->sample->right, pos, 0, 0, quality);
+	playing->resampler[1].pickup_data = playing->resampler[0].pickup_data = playing;
+	playing->time_lost = 0;
+	playing->flags &= ~IT_PLAYING_DEAD;
+	it_playing_update_resamplers(playing);
+}
+
+static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel);
+
+/* Should we only be retriggering short samples on XM? */
+
+static void update_retrig(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel)
+{
+	if (channel->xm_retrig) {
+		channel->retrig_tick--;
+		if (channel->retrig_tick <= 0) {
+			if (channel->playing) {
+				it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+				channel->playing->declick_stage = 0;
+				channel->playing->declick_volume = 1.f / 256.f;
+#endif
+			} else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel);
+			channel->retrig_tick = channel->xm_retrig;
+		}
+	} else if (channel->retrig & 0x0F) {
+		channel->retrig_tick--;
+		if (channel->retrig_tick <= 0) {
+			if (channel->retrig < 0x10) {
+			} else if (channel->retrig < 0x20) {
+				channel->volume--;
+				if (channel->volume > 64) channel->volume = 0;
+			} else if (channel->retrig < 0x30) {
+				channel->volume -= 2;
+				if (channel->volume > 64) channel->volume = 0;
+			} else if (channel->retrig < 0x40) {
+				channel->volume -= 4;
+				if (channel->volume > 64) channel->volume = 0;
+			} else if (channel->retrig < 0x50) {
+				channel->volume -= 8;
+				if (channel->volume > 64) channel->volume = 0;
+			} else if (channel->retrig < 0x60) {
+				channel->volume -= 16;
+				if (channel->volume > 64) channel->volume = 0;
+			} else if (channel->retrig < 0x70) {
+				channel->volume <<= 1;
+				channel->volume /= 3;
+			} else if (channel->retrig < 0x80) {
+				channel->volume >>= 1;
+			} else if (channel->retrig < 0x90) {
+			} else if (channel->retrig < 0xA0) {
+				channel->volume++;
+				if (channel->volume > 64) channel->volume = 64;
+			} else if (channel->retrig < 0xB0) {
+				channel->volume += 2;
+				if (channel->volume > 64) channel->volume = 64;
+			} else if (channel->retrig < 0xC0) {
+				channel->volume += 4;
+				if (channel->volume > 64) channel->volume = 64;
+			} else if (channel->retrig < 0xD0) {
+				channel->volume += 8;
+				if (channel->volume > 64) channel->volume = 64;
+			} else if (channel->retrig < 0xE0) {
+				channel->volume += 16;
+				if (channel->volume > 64) channel->volume = 64;
+			} else if (channel->retrig < 0xF0) {
+				channel->volume *= 3;
+				channel->volume >>= 1;
+				if (channel->volume > 64) channel->volume = 64;
+			} else {
+				channel->volume <<= 1;
+				if (channel->volume > 64) channel->volume = 64;
+			}
+			if (channel->playing) {
+				it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+				channel->playing->declick_stage = 0;
+				channel->playing->declick_volume = 1.f / 256.f;
+#endif
+			} else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel);
+			channel->retrig_tick = channel->retrig & 0x0F;
+		}
+	}
+}
+
+
+
+static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	int i;
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+		IT_PLAYING *playing = channel->playing;
+
+		if (playing) {
+			playing->vibrato_time += playing->vibrato_n *
+			                         (playing->vibrato_speed << 2);
+			playing->tremolo_time += playing->tremolo_speed << 2;
+			playing->panbrello_time += playing->panbrello_speed;
+			if (playing->panbrello_waveform == 3)
+				playing->panbrello_random = (rand() % 129) - 64;
+		}
+	}
+}
+
+
+
+static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	int i;
+
+	if (sigrenderer->globalvolslide) {
+		sigrenderer->globalvolume += sigrenderer->globalvolslide;
+		if (sigrenderer->globalvolume > 128) {
+			if (sigrenderer->globalvolslide >= 0)
+				sigrenderer->globalvolume = 128;
+			else
+				sigrenderer->globalvolume = 0;
+		}
+	}
+
+	if (sigrenderer->temposlide) {
+		sigrenderer->tempo += sigrenderer->temposlide;
+		if (sigrenderer->tempo < 32) {
+			if (sigrenderer->temposlide >= 0)
+				sigrenderer->tempo = 255;
+			else
+				sigrenderer->tempo = 32;
+		}
+	}
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+		IT_PLAYING *playing = channel->playing;
+
+		if (channel->xm_volslide) {
+			channel->volume += channel->xm_volslide;
+			if (channel->volume > 64) {
+				if (channel->xm_volslide >= 0)
+					channel->volume = 64;
+				else
+					channel->volume = 0;
+			}
+		}
+
+		if (channel->volslide) {
+			channel->volume += channel->volslide;
+			if (channel->volume > 64) {
+				if (channel->volslide >= 0)
+					channel->volume = 64;
+				else
+					channel->volume = 0;
+			}
+		}
+
+		if (channel->panslide) {
+			if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) {
+				if (IT_IS_SURROUND(channel->pan))
+				{
+					channel->pan = 32;
+					channel->truepan = 32 + 128 * 64;
+				}
+				if (channel->panslide == -128)
+					channel->truepan = 32;
+				else
+					channel->truepan = MID(32, channel->truepan + channel->panslide*64, 32+255*64);
+			} else {
+				if (IT_IS_SURROUND(channel->pan))
+				{
+					channel->pan = 32;
+				}
+				channel->pan += channel->panslide;
+				if (channel->pan > 64) {
+					if (channel->panslide >= 0)
+						channel->pan = 64;
+					else
+						channel->pan = 0;
+				}
+				channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+			}
+		}
+
+		if (channel->channelvolslide) {
+			channel->channelvolume += channel->channelvolslide;
+			if (channel->channelvolume > 64) {
+				if (channel->channelvolslide >= 0)
+					channel->channelvolume = 64;
+				else
+					channel->channelvolume = 0;
+			}
+			if (channel->playing)
+				channel->playing->channel_volume = channel->channelvolume;
+		}
+
+		update_tremor(channel);
+
+		channel->arpeggio = (channel->arpeggio << 4) | (channel->arpeggio >> 8);
+		channel->arpeggio &= 0xFFF;
+
+		update_retrig(sigrenderer, channel);
+
+		if (playing) {
+			playing->slide += channel->portamento;
+
+			if (channel->ptm_toneslide) {
+				if (--channel->toneslide_tick == 0) {
+					channel->toneslide_tick = channel->ptm_toneslide;
+					playing->note += channel->toneslide;
+					if (playing->note >= 120) {
+						if (channel->toneslide < 0) playing->note = 0;
+						else playing->note = 119;
+					}
+					channel->note = channel->truenote = playing->note;
+					if (channel->toneslide_retrig) {
+						it_playing_reset_resamplers(playing, 0);
+#ifdef END_RAMPING
+						playing->declick_stage = 0;
+						playing->declick_volume = 1.f / 256.f;
+#endif
+					}
+				}
+			}
+
+			if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) {
+				if (channel->toneporta && channel->destnote < 120) {
+					int currpitch = ((playing->note - 60) << 8) + playing->slide;
+					int destpitch = (channel->destnote - 60) << 8;
+					if (currpitch > destpitch) {
+						currpitch -= channel->toneporta;
+						if (currpitch < destpitch) {
+							currpitch = destpitch;
+							channel->destnote = IT_NOTE_OFF;
+						}
+					} else if (currpitch < destpitch) {
+						currpitch += channel->toneporta;
+						if (currpitch > destpitch) {
+							currpitch = destpitch;
+							channel->destnote = IT_NOTE_OFF;
+						}
+					}
+					playing->slide = currpitch - ((playing->note - 60) << 8);
+				}
+			} else {
+				if (channel->toneporta && channel->destnote < 120) {
+					float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR);
+
+					float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note);
+					/* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */
+
+					float deltaslid = deltanote - playing->slide * amiga_multiplier;
+
+					float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote);
+					if (deltaslid < destdelta) {
+						playing->slide -= channel->toneporta;
+						deltaslid = deltanote - playing->slide * amiga_multiplier;
+						if (deltaslid > destdelta) {
+							playing->note = channel->destnote;
+							playing->slide = 0;
+							channel->destnote = IT_NOTE_OFF;
+						}
+					} else {
+						playing->slide += channel->toneporta;
+						deltaslid = deltanote - playing->slide * amiga_multiplier;
+						if (deltaslid < destdelta) {
+							playing->note = channel->destnote;
+							playing->slide = 0;
+							channel->destnote = IT_NOTE_OFF;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	update_smooth_effects(sigrenderer);
+}
+
+
+static void it_note_off(IT_PLAYING *playing);
+
+// This function should be renamed; it doesn't do the 'Update Pattern Variables' operation ittech.txt describes
+/* Returns 1 if a pattern loop is happening. */
+static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+	if (entry->mask & IT_ENTRY_EFFECT) {
+		switch (entry->effect) {
+			case IT_JUMP_TO_ORDER:
+				sigrenderer->breakrow = 0;
+				sigrenderer->processorder = entry->effectvalue - 1;
+				sigrenderer->processrow = 0xFFFE;
+				break;
+
+			case IT_S:
+				{
+					unsigned char effectvalue = entry->effectvalue;
+					if (effectvalue == 0)
+						effectvalue = channel->lastS;
+					channel->lastS = effectvalue;
+					switch (effectvalue >> 4) {
+#if 1
+						case IT_S7:
+							{
+								if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
+								{
+									int i;
+									switch (effectvalue & 15)
+									{
+									case 0: /* cut background notes */
+										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+										{
+											IT_PLAYING * playing = sigrenderer->playing[i];
+											if (playing && channel == playing->channel)
+											{
+#ifdef RAMP_DOWN
+												playing->declick_stage = 2;
+#else
+												free(playing);
+												sigrenderer->playing[i] = NULL;
+#endif
+												if (channel->playing == playing) channel->playing = NULL;
+											}
+										}
+										break;
+									case 1: /* release background notes */
+										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+										{
+											IT_PLAYING * playing = sigrenderer->playing[i];
+											if (playing && channel == playing->channel && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+											{
+												it_note_off(playing);
+											}
+										}
+										break;
+									case 2: /* fade background notes */
+										for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+										{
+											IT_PLAYING * playing = sigrenderer->playing[i];
+											if (playing && channel == playing->channel)
+											{
+												//playing->flags &= IT_PLAYING_SUSTAINOFF;
+												playing->flags |= IT_PLAYING_FADING;
+											}
+										}
+										break;
+									}
+								}
+							}
+							break;
+#endif
+						case IT_S_PATTERN_LOOP:
+							{
+								unsigned char v = effectvalue & 15;
+								if (v == 0) {
+#ifdef BIT_ARRAY_BULLSHIT
+									if (!channel->played_patjump)
+										channel->played_patjump = bit_array_create(256);
+									else {
+										if (channel->played_patjump_order != sigrenderer->order)
+											bit_array_reset(channel->played_patjump);
+									}
+									channel->played_patjump_order = sigrenderer->order;
+#endif
+									channel->pat_loop_row = sigrenderer->processrow;
+								} else {
+									if (channel->pat_loop_count == 0) {
+#ifdef BIT_ARRAY_BULLSHIT
+										/* wft, uninitialized and no start marker yet... */
+										if (channel->played_patjump_order == 0xFFFE) {
+											int n;
+											bit_array_destroy(channel->played_patjump);
+											channel->played_patjump = bit_array_create(256);
+											for (n = 0; n < 256; n++)
+												bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + n);
+											channel->played_patjump_order = sigrenderer->order;
+										} else if (channel->played_patjump_order == sigrenderer->order) {
+											bit_array_set(channel->played_patjump, sigrenderer->row);
+											bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256);
+											bit_array_reset(channel->played_patjump);
+										}
+#endif
+										channel->pat_loop_count = v;
+										sigrenderer->breakrow = channel->pat_loop_row;
+										if (!(sigrenderer->sigdata->flags & IT_WAS_AN_XM) || sigrenderer->processrow != 0xFFFE) {
+											sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */
+											sigrenderer->processrow = 0xFFFE;
+										}
+										return 1;
+									} else if (--channel->pat_loop_count) {
+#ifdef BIT_ARRAY_BULLSHIT
+										if (channel->played_patjump_order == sigrenderer->order) {
+											bit_array_set(channel->played_patjump, sigrenderer->row);
+											bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256);
+											bit_array_reset(channel->played_patjump);
+										}
+#endif
+										sigrenderer->breakrow = channel->pat_loop_row;
+										if (!(sigrenderer->sigdata->flags & IT_WAS_AN_XM) || sigrenderer->processrow != 0xFFFE) {
+											sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */
+											sigrenderer->processrow = 0xFFFE;
+										}
+										return 1;
+									} else if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+										sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */
+									} else {
+										channel->pat_loop_row = sigrenderer->processrow + 1;
+									}
+#ifdef BIT_ARRAY_BULLSHIT
+									/*channel->played_patjump_order |= 0x8000;*/
+									if (channel->played_patjump_order == sigrenderer->order) {
+										bit_array_destroy(channel->played_patjump);
+										channel->played_patjump = 0;
+										channel->played_patjump_order = 0xFFFE;
+									}
+									bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+#endif
+								}
+							}
+							break;
+						case IT_S_PATTERN_DELAY:
+							sigrenderer->rowcount = 1 + (effectvalue & 15);
+							break;
+					}
+				}
+		}
+	}
+
+	return 0;
+}
+
+
+
+/* This function guarantees that channel->sample will always be valid if it
+ * is nonzero. In other words, to check if it is valid, simply check if it is
+ * nonzero.
+ */
+static void instrument_to_sample(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+	if (sigdata->flags & IT_USE_INSTRUMENTS) {
+		if (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments) {
+			if (channel->note < 120) {
+				channel->sample = sigdata->instrument[channel->instrument-1].map_sample[channel->note];
+				channel->truenote = sigdata->instrument[channel->instrument-1].map_note[channel->note];
+			} else
+				channel->sample = 0;
+		} else
+			channel->sample = 0;
+	} else {
+		channel->sample = channel->instrument;
+		channel->truenote = channel->note;
+	}
+	if (!(channel->sample >= 1 && channel->sample <= sigdata->n_samples && (sigdata->sample[channel->sample-1].flags & IT_SAMPLE_EXISTS) /* && sigdata->sample[channel->sample-1].C5_speed*/))
+		channel->sample = 0;
+}
+
+
+
+static void fix_sample_looping(IT_PLAYING *playing)
+{
+	if ((playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) ==
+	                              (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) {
+		if (playing->resampler[0].dir < 0) {
+			playing->resampler[1].pos = playing->resampler[0].pos = (playing->sample->sus_loop_end << 1) - 1 - playing->resampler[0].pos;
+			playing->resampler[1].subpos = playing->resampler[0].subpos ^= 65535;
+			playing->resampler[1].dir = playing->resampler[0].dir = 1;
+		}
+
+		playing->resampler[1].pos = playing->resampler[0].pos += playing->time_lost;
+	}
+}
+
+
+
+static void it_compatible_gxx_retrigger(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+	int flags = 0;
+	if (channel->sample) {
+		if (sigdata->flags & IT_USE_INSTRUMENTS) {
+			if (channel->playing->env_instrument == &sigdata->instrument[channel->instrument-1]) {
+				if (channel->playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_CARRY)
+					flags |= 1;
+				if (channel->playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_CARRY)
+					flags |= 2;
+				if (channel->playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_CARRY)
+					flags |= 4;
+			}
+		}
+	}
+	if (!(flags & 1)) {
+		channel->playing->volume_envelope.next_node = 0;
+		channel->playing->volume_envelope.tick = 0;
+	}
+	if (!(flags & 2)) {
+		channel->playing->pan_envelope.next_node = 0;
+		channel->playing->pan_envelope.tick = 0;
+	}
+	if (!(flags & 4)) {
+		channel->playing->pitch_envelope.next_node = 0;
+		channel->playing->pitch_envelope.tick = 0;
+	}
+	channel->playing->fadeoutcount = 1024;
+	// Should we remove IT_PLAYING_BACKGROUND? Test with sample with sustain loop...
+	channel->playing->flags &= ~(IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING | IT_PLAYING_DEAD);
+	it_playing_update_resamplers(channel->playing);
+
+	if (!flags && channel->sample)
+		if (sigdata->flags & IT_USE_INSTRUMENTS)
+			channel->playing->env_instrument = &sigdata->instrument[channel->instrument-1];
+}
+
+
+
+static void it_note_off(IT_PLAYING *playing)
+{
+	if (playing) {
+		playing->enabled_envelopes |= IT_ENV_VOLUME;
+		playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF;
+		fix_sample_looping(playing);
+		it_playing_update_resamplers(playing);
+		if (playing->instrument)
+			if ((playing->instrument->volume_envelope.flags & (IT_ENVELOPE_ON | IT_ENVELOPE_LOOP_ON)) != IT_ENVELOPE_ON)
+				playing->flags |= IT_PLAYING_FADING;
+	}
+}
+
+
+
+static void xm_note_off(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+	if (channel->playing) {
+		if (!(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON))
+			//if (!(entry->mask & IT_ENTRY_INSTRUMENT))
+			// dunno what that was there for ...
+				channel->volume = 0;
+		channel->playing->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING;
+		it_playing_update_resamplers(channel->playing);
+	}
+}
+
+
+
+static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel)
+{
+	IT_PLAYING_ENVELOPE volume_envelope;
+	IT_PLAYING_ENVELOPE pan_envelope;
+	IT_PLAYING_ENVELOPE pitch_envelope;
+
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+	unsigned char nna;
+	int i, flags = 0;
+
+	if (channel->playing) {
+#ifdef INVALID_NOTES_CAUSE_NOTE_CUT
+		if (channel->note == IT_NOTE_OFF)
+			nna = NNA_NOTE_OFF;
+		else if (channel->note >= 120 || !channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD))
+			nna = NNA_NOTE_CUT;
+		else if (channel->new_note_action != 0xFF)
+		{
+			nna = channel->new_note_action;
+		}
+		else
+			nna = channel->playing->instrument->new_note_action;
+#else
+		if (channel->note == IT_NOTE_CUT)
+			nna = NNA_NOTE_CUT;
+		if (channel->note >= 120)
+			nna = NNA_NOTE_OFF;
+		else if (!channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD))
+			nna = NNA_NOTE_CUT;
+		else if (channel->new_note_action != 0xFF)
+		{
+			nna = channel->new_note_action;
+		}
+		else
+			nna = channel->playing->instrument->new_note_action;
+#endif
+
+		if ((sigdata->flags & IT_USE_INSTRUMENTS) && (channel->playing->enabled_envelopes) && channel->playing->instnum == channel->instrument) {
+			IT_PLAYING * playing = channel->playing;
+			IT_INSTRUMENT * inst = &sigdata->instrument[channel->instrument-1];
+			if ((playing->enabled_envelopes & IT_ENV_VOLUME) && (inst->volume_envelope.flags & IT_ENVELOPE_CARRY)) {
+				flags |= 1;
+				volume_envelope = playing->volume_envelope;
+			}
+			if ((playing->enabled_envelopes & IT_ENV_PANNING) && (inst->pan_envelope.flags & IT_ENVELOPE_CARRY)) {
+				flags |= 2;
+				pan_envelope = playing->pan_envelope;
+			}
+			if ((playing->enabled_envelopes & IT_ENV_PITCH) && (inst->pitch_envelope.flags & IT_ENVELOPE_CARRY)) {
+				flags |= 4;
+				pitch_envelope = playing->pitch_envelope;
+			}
+		}
+
+		switch (nna) {
+			case NNA_NOTE_CUT:
+#ifdef RAMP_DOWN
+				channel->playing->declick_stage = 2;
+#else
+				free(channel->playing);
+				channel->playing = NULL;
+#endif
+				break;
+			case NNA_NOTE_OFF:
+				it_note_off(channel->playing);
+				break;
+			case NNA_NOTE_FADE:
+				channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING;
+				break;
+		}
+	}
+
+	channel->new_note_action = 0xFF;
+
+	if (channel->sample == 0 || channel->note >= 120)
+		return;
+
+	channel->destnote = IT_NOTE_OFF;
+
+	if (channel->playing) {
+		for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+			if (!sigrenderer->playing[i]) {
+				sigrenderer->playing[i] = channel->playing;
+				channel->playing = NULL;
+				break;
+			}
+		}
+
+		if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
+		{
+			for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+				IT_PLAYING * playing = sigrenderer->playing[i];
+				if (playing && playing->channel == channel && playing->instrument->dup_check_type) {
+					int match = 0;
+					switch (playing->instrument->dup_check_type)
+					{
+					case DCT_NOTE:
+						match = (channel->truenote == playing->note);
+					case DCT_SAMPLE:
+						match = match && (channel->sample == playing->sampnum);
+					case DCT_INSTRUMENT:
+						match = match && (channel->instrument == playing->instnum);
+						break;
+					}
+
+					if (match)
+					{
+						switch (playing->instrument->dup_check_action)
+						{
+						case DCA_NOTE_CUT:
+#ifdef RAMP_DOWN
+							playing->declick_stage = 2;
+#else
+							free(playing);
+							sigrenderer->playing[i] = NULL;
+#endif
+							if (channel->playing == playing) channel->playing = NULL;
+							break;
+						case DCA_NOTE_OFF:
+							if (!(playing->flags & IT_PLAYING_SUSTAINOFF))
+								it_note_off(playing);
+							break;
+						case DCA_NOTE_FADE:
+							playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING;
+							break;
+						}
+					}
+				}
+			}
+		}
+
+/** WARNING - come up with some more heuristics for replacing old notes */
+#if 0
+		if (channel->playing) {
+			for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+				if (sigrenderer->playing[i]->flags & IT_PLAYING_BACKGROUND) {
+					write_seqtime();
+					sequence_c(SEQUENCE_STOP_SIGNAL);
+					sequence_c(i);
+					channel->VChannel = &module->VChannel[i];
+					break;
+				}
+			}
+		}
+#endif
+	}
+
+	if (channel->playing)
+		free(channel->playing);
+
+	channel->playing = malloc(sizeof(*channel->playing));
+
+	if (!channel->playing)
+		return;
+
+	if (!flags && sigdata->flags & IT_USE_INSTRUMENTS) {
+		for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+			IT_PLAYING * playing = sigrenderer->playing[i];
+			if (playing && (playing->enabled_envelopes) && playing->instnum == channel->instrument) {
+				IT_INSTRUMENT * inst = &sigdata->instrument[channel->instrument-1];
+				if ((playing->enabled_envelopes & IT_ENV_VOLUME) && (inst->volume_envelope.flags & IT_ENVELOPE_CARRY)) {
+					flags |= 1;
+					volume_envelope = playing->volume_envelope;
+				}
+				if ((playing->enabled_envelopes & IT_ENV_PANNING) && (inst->pan_envelope.flags & IT_ENVELOPE_CARRY)) {
+					flags |= 2;
+					pan_envelope = playing->pan_envelope;
+				}
+				if ((playing->enabled_envelopes & IT_ENV_PITCH) && (inst->pitch_envelope.flags & IT_ENVELOPE_CARRY)) {
+					flags |= 4;
+					pitch_envelope = playing->pitch_envelope;
+				}
+				break;
+			}
+		}
+	}				
+
+	channel->playing->flags = 0;
+	channel->playing->resampling_quality = sigrenderer->resampling_quality;
+	channel->playing->channel = channel;
+	channel->playing->sample = &sigdata->sample[channel->sample-1];
+	if (sigdata->flags & IT_USE_INSTRUMENTS)
+		channel->playing->instrument = &sigdata->instrument[channel->instrument-1];
+	else
+		channel->playing->instrument = NULL;
+	channel->playing->env_instrument = channel->playing->instrument;
+	channel->playing->sampnum = channel->sample;
+	channel->playing->instnum = channel->instrument;
+#ifdef END_RAMPING
+	channel->playing->declick_stage = 0;
+	channel->playing->declick_volume = 1.f / 256.f;
+#endif
+	channel->playing->channel_volume = channel->channelvolume;
+	channel->playing->ramp_volume[0] = 31337.0; /* special */
+	channel->playing->note = channel->truenote;
+	channel->playing->enabled_envelopes = 0;
+	channel->playing->volume_offset = 0;
+	channel->playing->panning_offset = 0;
+	if (sigdata->flags & IT_USE_INSTRUMENTS) {
+		IT_PLAYING * playing = channel->playing;
+		IT_INSTRUMENT * instrument = playing->instrument;
+		if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME;
+		if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING;
+		if (instrument->pitch_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PITCH;
+		if (instrument->random_volume) playing->volume_offset = (rand() % (instrument->random_volume * 2 + 1)) - instrument->random_volume;
+		if (instrument->random_pan) playing->panning_offset = (rand() % (instrument->random_pan * 2 + 1)) - instrument->random_pan;
+	}
+	channel->playing->filter_cutoff = 127;
+	channel->playing->filter_resonance = 0;
+	channel->playing->true_filter_cutoff = 127 << 8;
+	channel->playing->true_filter_resonance = 0;
+	channel->playing->vibrato_speed = 0;
+	channel->playing->vibrato_depth = 0;
+	channel->playing->vibrato_n = 0;
+	channel->playing->vibrato_time = 0;
+	channel->playing->vibrato_waveform = channel->vibrato_waveform;
+	channel->playing->tremolo_speed = 0;
+	channel->playing->tremolo_depth = 0;
+	channel->playing->tremolo_time = 0;
+	channel->playing->tremolo_waveform = channel->tremolo_waveform;
+	channel->playing->panbrello_speed = 0;
+	channel->playing->panbrello_depth = 0;
+	channel->playing->panbrello_time = 0;
+	channel->playing->panbrello_waveform = channel->panbrello_waveform;
+	channel->playing->panbrello_random = 0;
+	channel->playing->sample_vibrato_time = 0;
+	channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform;
+	channel->playing->sample_vibrato_depth = 0;
+	channel->playing->slide = 0;
+	channel->playing->finetune = 0;
+
+	if (flags & 1) {
+		channel->playing->volume_envelope = volume_envelope;
+	} else {
+		channel->playing->volume_envelope.next_node = 0;
+		channel->playing->volume_envelope.tick = 0;
+	}
+	if (flags & 2) {
+		channel->playing->pan_envelope = pan_envelope;
+	} else {
+		channel->playing->pan_envelope.next_node = 0;
+		channel->playing->pan_envelope.tick = 0;
+	}
+	if (flags & 4) {
+		channel->playing->pitch_envelope = pitch_envelope;
+	} else {
+		channel->playing->pitch_envelope.next_node = 0;
+		channel->playing->pitch_envelope.tick = 0;
+	}
+	channel->playing->fadeoutcount = 1024;
+	it_reset_filter_state(&channel->playing->filter_state[0]);
+	it_reset_filter_state(&channel->playing->filter_state[1]);
+	it_playing_reset_resamplers(channel->playing, 0);
+
+	/** WARNING - is everything initialised? */
+}
+
+
+
+static void get_default_volpan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+	if (channel->sample == 0)
+		return;
+
+	channel->volume = sigdata->sample[channel->sample-1].default_volume;
+
+	if (sigdata->flags & IT_WAS_AN_XM) {
+		if (!(sigdata->flags & IT_WAS_A_MOD))
+			channel->truepan = 32 + sigdata->sample[channel->sample-1].default_pan*64;
+		return;
+	}
+
+	{
+		int pan = sigdata->sample[channel->sample-1].default_pan;
+		if (pan >= 128 && pan <= 192) {
+			channel->pan = pan - 128;
+			return;
+		}
+	}
+
+	if (sigdata->flags & IT_USE_INSTRUMENTS) {
+		IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1];
+		if (instrument->default_pan <= 64)
+			channel->pan = instrument->default_pan;
+		if (instrument->filter_cutoff >= 128)
+			channel->filter_cutoff = instrument->filter_cutoff - 128;
+		if (instrument->filter_resonance >= 128)
+			channel->filter_resonance = instrument->filter_resonance - 128;
+	}
+}
+
+
+
+static void get_true_pan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel)
+{
+	channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+
+	if (!IT_IS_SURROUND_SHIFTED(channel->truepan) && (sigdata->flags & IT_USE_INSTRUMENTS)) {
+		IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1];
+		int truepan = channel->truepan;
+		truepan += (channel->note - instrument->pp_centre) * instrument->pp_separation << (IT_ENVELOPE_SHIFT - 3);
+		channel->truepan = MID(0, truepan, 64 << IT_ENVELOPE_SHIFT);
+	}
+}
+
+
+
+static void post_process_it_volpan(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+	if (entry->mask & IT_ENTRY_VOLPAN) {
+		if (entry->volpan <= 84) {
+			/* Volume */
+			/* Fine volume slide up */
+			/* Fine volume slide down */
+		} else if (entry->volpan <= 94) {
+			/* Volume slide up */
+			unsigned char v = entry->volpan - 85;
+			if (v == 0)
+				v = channel->lastvolslide;
+			channel->lastvolslide = v;
+			/* = effect Dx0 where x == entry->volpan - 85 */
+			channel->volslide = v;
+		} else if (entry->volpan <= 104) {
+			/* Volume slide down */
+			unsigned char v = entry->volpan - 95;
+			if (v == 0)
+				v = channel->lastvolslide;
+			channel->lastvolslide = v;
+			/* = effect D0x where x == entry->volpan - 95 */
+			channel->volslide = -v;
+		} else if (entry->volpan <= 114) {
+			/* Portamento down */
+			unsigned char v = (entry->volpan - 105) << 2;
+			if (v == 0)
+				v = channel->lastEF;
+			channel->lastEF = v;
+			channel->portamento -= v << 4;
+		} else if (entry->volpan <= 124) {
+			/* Portamento up */
+			unsigned char v = (entry->volpan - 115) << 2;
+			if (v == 0)
+				v = channel->lastEF;
+			channel->lastEF = v;
+			channel->portamento += v << 4;
+		} else if (entry->volpan <= 202) {
+			/* Pan */
+			/* Tone Portamento */
+		} else if (entry->volpan <= 212) {
+			/* Vibrato */
+			/* This is unaffected by IT_OLD_EFFECTS. However, if v == 0, then any doubling of depth that happened before (with Hxy in the effect column) will be preserved. */
+			unsigned char v = entry->volpan - 203;
+			if (v == 0)
+				v = channel->lastHdepth;
+			else {
+				v <<= 2;
+				channel->lastHdepth = v;
+			}
+			if (channel->playing) {
+				channel->playing->vibrato_speed = channel->lastHspeed;
+				channel->playing->vibrato_depth = v;
+				channel->playing->vibrato_n++;
+			}
+		}
+	}
+}
+
+
+
+static void it_send_midi(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel, unsigned char midi_byte)
+{
+	if (sigrenderer->callbacks->midi)
+		if ((*sigrenderer->callbacks->midi)(sigrenderer->callbacks->midi_data, channel - sigrenderer->channel, midi_byte))
+			return;
+
+	switch (channel->midi_state) {
+		case 4: /* Ready to receive resonance parameter */
+			if (midi_byte < 0x80) channel->filter_resonance = midi_byte;
+			channel->midi_state = 0;
+			break;
+		case 3: /* Ready to receive cutoff parameter */
+			if (midi_byte < 0x80) channel->filter_cutoff = midi_byte;
+			channel->midi_state = 0;
+			break;
+		case 2: /* Ready for byte specifying which parameter will follow */
+			if (midi_byte == 0) /* Cutoff */
+				channel->midi_state = 3;
+			else if (midi_byte == 1) /* Resonance */
+				channel->midi_state = 4;
+			else
+				channel->midi_state = 0;
+			break;
+		default: /* Counting initial F0 bytes */
+			switch (midi_byte) {
+				case 0xF0:
+					channel->midi_state++;
+					break;
+				case 0xFA:
+				case 0xFC:
+				case 0xFF:
+					/* Reset filter parameters for all channels */
+					{
+						int i;
+						for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+							sigrenderer->channel[i].filter_cutoff = 127;
+							sigrenderer->channel[i].filter_resonance = 0;
+							//// should we be resetting channel[i].playing->filter_* here?
+						}
+					}
+					/* Fall through */
+				default:
+					channel->midi_state = 0;
+					break;
+			}
+	}
+}
+
+extern const char xm_convert_vibrato[];
+
+/* Returns 1 if a callback caused termination of playback. */
+static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx)
+{
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+
+	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+	if (entry->mask & IT_ENTRY_EFFECT) {
+		switch (entry->effect) {
+/*
+Notes about effects (as compared to other module formats)
+
+C               This is now in *HEX*. (Used to be in decimal in ST3)
+E/F/G/H/U       You need to check whether the song uses Amiga/Linear slides.
+H/U             Vibrato in Impulse Tracker is two times finer than in
+                any other tracker and is updated EVERY tick.
+                If "Old Effects" is *ON*, then the vibrato is played in the
+                normal manner (every non-row tick and normal depth)
+E/F/G           These commands ALL share the same memory.
+Oxx             Offsets to samples are to the 'xx00th' SAMPLE. (ie. for
+                16 bit samples, the offset is xx00h*2)
+                Oxx past the sample end will be ignored, unless "Old Effects"
+                is ON, in which case the Oxx will play from the end of the
+                sample.
+Yxy             This uses a table 4 times larger (hence 4 times slower) than
+                vibrato or tremelo. If the waveform is set to random, then
+                the 'speed' part of the command is interpreted as a delay.
+*/
+			case IT_SET_SPEED:
+				if (entry->effectvalue)
+				{
+					/*if (entry->effectvalue == 255)
+						if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data))
+							return 1;*/
+					sigrenderer->tick = sigrenderer->speed = entry->effectvalue;
+				}
+				else if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) {
+#ifdef BIT_ARRAY_BULLSHIT
+					bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+#endif
+					sigrenderer->speed = 0;
+					if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data))
+						return 1;
+				}
+				break;
+
+			case IT_BREAK_TO_ROW:
+				if (ignore_cxx) break;
+				sigrenderer->breakrow = entry->effectvalue;
+				sigrenderer->processorder = sigrenderer->order;
+				sigrenderer->processrow = 0xFFFE;
+				break;
+
+			case IT_VOLSLIDE_VIBRATO:
+				if (channel->playing) {
+					channel->playing->vibrato_speed = channel->lastHspeed;
+					channel->playing->vibrato_depth = channel->lastHdepth;
+					channel->playing->vibrato_n++;
+				}
+				/* Fall through and process volume slide. */
+			case IT_VOLUME_SLIDE:
+			case IT_VOLSLIDE_TONEPORTA:
+				/* The tone portamento component is handled elsewhere. */
+				{
+					unsigned char v = entry->effectvalue;
+					if (!(sigdata->flags & IT_WAS_A_MOD)) {
+						if (v == 0)
+							v = channel->lastDKL;
+						channel->lastDKL = v;
+					}
+					if ((v & 0x0F) == 0) { /* Dx0 */
+						channel->volslide = v >> 4;
+						if (channel->volslide == 15 && !(sigdata->flags & IT_WAS_AN_XM)) {
+							channel->volume += 15;
+							if (channel->volume > 64) channel->volume = 64;
+						}
+					} else if ((v & 0xF0) == 0) { /* D0x */
+						channel->volslide = -v;
+						if (channel->volslide == -15 && !(sigdata->flags & IT_WAS_AN_XM)) {
+							channel->volume -= 15;
+							if (channel->volume > 64) channel->volume = 0;
+						}
+					} else if ((v & 0x0F) == 0x0F) { /* DxF */
+						channel->volume += v >> 4;
+						if (channel->volume > 64) channel->volume = 64;
+					} else if ((v & 0xF0) == 0xF0) { /* DFx */
+						channel->volume -= v & 15;
+						if (channel->volume > 64) channel->volume = 0;
+					}
+				}
+				break;
+			case IT_XM_FINE_VOLSLIDE_DOWN:
+				{
+					unsigned char v = entry->effectvalue;
+					if (v == 0)
+						v = channel->xm_lastEB;
+					channel->xm_lastEB = v;
+					channel->volume -= v;
+					if (channel->volume > 64) channel->volume = 0;
+				}
+				break;
+			case IT_XM_FINE_VOLSLIDE_UP:
+				{
+					unsigned char v = entry->effectvalue;
+					if (v == 0)
+						v = channel->xm_lastEA;
+					channel->xm_lastEA = v;
+					channel->volume += v;
+					if (channel->volume > 64) channel->volume = 64;
+				}
+				break;
+			case IT_PORTAMENTO_DOWN:
+				{
+					unsigned char v = entry->effectvalue;
+					if (sigdata->flags & IT_WAS_AN_XM) {
+						if (!(sigdata->flags & IT_WAS_A_MOD)) {
+							if (v == 0xF0)
+								v |= channel->xm_lastE2;
+							else if (v >= 0xF0)
+								channel->xm_lastE2 = v & 15;
+							else if (v == 0xE0)
+								v |= channel->xm_lastX2;
+							else
+								channel->xm_lastX2 = v & 15;
+						}
+					} else {
+						if (v == 0)
+							v = channel->lastEF;
+						channel->lastEF = v;
+					}
+					if (channel->playing) {
+						if ((v & 0xF0) == 0xF0)
+							channel->playing->slide -= (v & 15) << 4;
+						else if ((v & 0xF0) == 0xE0)
+							channel->playing->slide -= (v & 15) << 2;
+						else
+							channel->portamento -= v << 4;
+					}
+				}
+				break;
+			case IT_PORTAMENTO_UP:
+				{
+					unsigned char v = entry->effectvalue;
+					if (sigdata->flags & IT_WAS_AN_XM) {
+						if (!(sigdata->flags & IT_WAS_A_MOD)) {
+							if (v == 0xF0)
+								v |= channel->xm_lastE1;
+							else if (v >= 0xF0)
+								channel->xm_lastE1 = v & 15;
+							else if (v == 0xE0)
+								v |= channel->xm_lastX1;
+							else
+								channel->xm_lastX1 = v & 15;
+						}
+					} else {
+						if (v == 0)
+							v = channel->lastEF;
+						channel->lastEF = v;
+					}
+					if (channel->playing) {
+						if ((v & 0xF0) == 0xF0)
+							channel->playing->slide += (v & 15) << 4;
+						else if ((v & 0xF0) == 0xE0)
+							channel->playing->slide += (v & 15) << 2;
+						else
+							channel->portamento += v << 4;
+					}
+				}
+				break;
+			case IT_XM_PORTAMENTO_DOWN:
+				{
+					unsigned char v = entry->effectvalue;
+					if (!(sigdata->flags & IT_WAS_A_MOD)) {
+						if (v == 0)
+							v = channel->lastJ;
+						channel->lastJ = v;
+					}
+					if (channel->playing)
+						channel->portamento -= v << 4;
+				}
+				break;
+			case IT_XM_PORTAMENTO_UP:
+				{
+					unsigned char v = entry->effectvalue;
+					if (!(sigdata->flags & IT_WAS_A_MOD)) {
+						if (v == 0)
+							v = channel->lastEF;
+						channel->lastEF = v;
+					}
+					if (channel->playing)
+						channel->portamento += v << 4;
+				}
+				break;
+			case IT_XM_KEY_OFF:
+				channel->key_off_count = entry->effectvalue;
+				if (!channel->key_off_count) xm_note_off(sigdata, channel);
+				break;
+			case IT_VIBRATO:
+				{
+					unsigned char speed = entry->effectvalue >> 4;
+					unsigned char depth = entry->effectvalue & 15;
+					if (speed == 0)
+						speed = channel->lastHspeed;
+					channel->lastHspeed = speed;
+					if (depth == 0)
+						depth = channel->lastHdepth;
+					else {
+						if (sigdata->flags & IT_OLD_EFFECTS)
+							depth <<= 3;
+						else
+							depth <<= 2;
+						channel->lastHdepth = depth;
+					}
+					if (channel->playing) {
+						channel->playing->vibrato_speed = speed;
+						channel->playing->vibrato_depth = depth;
+						channel->playing->vibrato_n++;
+					}
+				}
+				break;
+			case IT_TREMOR:
+				{
+					unsigned char v = entry->effectvalue;
+					if (v == 0)
+						v = channel->lastI;
+					else if (!(sigdata->flags & IT_OLD_EFFECTS)) {
+						if (v & 0xF0) v -= 0x10;
+						if (v & 0x0F) v -= 0x01;
+					}
+					channel->lastI = v;
+					channel->tremor_time |= 128;
+				}
+				update_tremor(channel);
+				break;
+			case IT_ARPEGGIO:
+				{
+					unsigned char v = entry->effectvalue;
+					/* XM files have no memory for arpeggio (000 = no effect)
+					 * and we use lastJ for portamento down instead.
+					 */
+					if (!(sigdata->flags & IT_WAS_AN_XM)) {
+						if (v == 0)
+							v = channel->lastJ;
+						channel->lastJ = v;
+					}
+					channel->arpeggio = v;
+				}
+				break;
+			case IT_SET_CHANNEL_VOLUME:
+				if (sigdata->flags & IT_WAS_AN_XM)
+					channel->volume = MIN(entry->effectvalue, 64);
+				else if (entry->effectvalue <= 64)
+					channel->channelvolume = entry->effectvalue;
+#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
+				else
+					channel->channelvolume = 64;
+#endif
+				if (channel->playing)
+					channel->playing->channel_volume = channel->channelvolume;
+				break;
+			case IT_CHANNEL_VOLUME_SLIDE:
+				{
+					unsigned char v = entry->effectvalue;
+					if (v == 0)
+						v = channel->lastN;
+					channel->lastN = v;
+					if ((v & 0x0F) == 0) { /* Nx0 */
+						channel->channelvolslide = v >> 4;
+					} else if ((v & 0xF0) == 0) { /* N0x */
+						channel->channelvolslide = -v;
+					} else {
+						if ((v & 0x0F) == 0x0F) { /* NxF */
+							channel->channelvolume += v >> 4;
+							if (channel->channelvolume > 64) channel->channelvolume = 64;
+						} else if ((v & 0xF0) == 0xF0) { /* NFx */
+							channel->channelvolume -= v & 15;
+							if (channel->channelvolume > 64) channel->channelvolume = 0;
+						} else
+							break;
+						if (channel->playing)
+							channel->playing->channel_volume = channel->channelvolume;
+					}
+				}
+				break;
+			case IT_SET_SAMPLE_OFFSET:
+				{
+					unsigned char v = entry->effectvalue;
+					/*if (sigdata->flags & IT_WAS_A_MOD) {
+						if (v == 0) break;
+					} else*/ {
+						if (v == 0)
+							v = channel->lastO;
+						channel->lastO = v;
+					}
+					/* Note: we set the offset even if tone portamento is
+					 * specified. Impulse Tracker does the same.
+					 */
+					if (entry->mask & IT_ENTRY_NOTE) {
+						if (channel->playing) {
+							int offset = ((int)channel->high_offset << 16) | ((int)v << 8);
+							IT_PLAYING *playing = channel->playing;
+							IT_SAMPLE *sample = playing->sample;
+							int end;
+							if ((sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+								end = sample->sus_loop_end;
+							else if (sample->flags & IT_SAMPLE_LOOP)
+								end = sample->loop_end;
+							else
+								end = sample->length;
+							if ((sigdata->flags & IT_WAS_A_PTM) && (sample->flags & IT_SAMPLE_16BIT))
+								offset >>= 1;
+							if (offset < end) {
+								it_playing_reset_resamplers(playing, offset);
+#ifdef END_RAMPING
+								playing->declick_stage = 0;
+								playing->declick_volume = 1.f / 256.f;
+#endif
+							} else if (sigdata->flags & IT_OLD_EFFECTS) {
+								it_playing_reset_resamplers(playing, end);
+#ifdef END_RAMPING
+								playing->declick_stage = 0;
+								playing->declick_volume = 1.f / 256.f;
+#endif
+							}
+						}
+					}
+				}
+				break;
+			case IT_PANNING_SLIDE:
+				/** JULIEN: guess what? the docs are wrong! (how unusual ;)
+				 * Pxy seems to memorize its previous value... and there
+				 * might be other mistakes like that... (sigh!)
+				 */
+				/** ENTHEH: umm... but... the docs say that Pxy memorises its
+				 * value... don't they? :o
+				 */
+				{
+					unsigned char v = entry->effectvalue;
+					int p = channel->truepan;
+					if (sigdata->flags & IT_WAS_AN_XM)
+					{
+						if (IT_IS_SURROUND(channel->pan))
+						{
+							channel->pan = 32;
+							p = 32 + 128 * 64;
+						}
+						p >>= 6;
+					}
+					else {
+						if (IT_IS_SURROUND(channel->pan)) p = 32 << 8;
+						p = (p + 128) >> 8;
+						channel->pan = p;
+					}
+					if (v == 0)
+						v = channel->lastP;
+					channel->lastP = v;
+					if ((v & 0x0F) == 0) { /* Px0 */
+						channel->panslide = -(v >> 4);
+					} else if ((v & 0xF0) == 0) { /* P0x */
+						channel->panslide = v;
+					} else if ((v & 0x0F) == 0x0F) { /* PxF */
+						p -= v >> 4;
+					} else if ((v & 0xF0) == 0xF0) { /* PFx */
+						p += v & 15;
+					}
+					if (sigdata->flags & IT_WAS_AN_XM)
+						channel->truepan = 32 + MID(0, p, 255) * 64;
+					else {
+						if (p < 0) p = 0;
+						else if (p > 64) p = 64;
+						channel->pan = p;
+						channel->truepan = p << 8;
+					}
+				}
+				break;
+			case IT_RETRIGGER_NOTE:
+				{
+					unsigned char v = entry->effectvalue;
+					if (sigdata->flags & IT_WAS_AN_XM) {
+						if ((v & 0x0F) == 0) v |= channel->lastQ & 0x0F;
+						if ((v & 0xF0) == 0) v |= channel->lastQ & 0xF0;
+					} else {
+						if (v == 0)
+							v = channel->lastQ;
+					}
+					channel->lastQ = v;
+					if ((v & 0x0F) == 0) v |= 0x01;
+					channel->retrig = v;
+					if (entry->mask & IT_ENTRY_NOTE) {
+						channel->retrig_tick = v & 0x0F;
+						/* Emulate a bug */
+						if (sigdata->flags & IT_WAS_AN_XM)
+							update_retrig(sigrenderer, channel);
+					} else
+						update_retrig(sigrenderer, channel);
+				}
+				break;
+			case IT_XM_RETRIGGER_NOTE:
+				channel->retrig_tick = channel->xm_retrig = entry->effectvalue;
+				if (entry->effectvalue == 0)
+					if (channel->playing) {
+						it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+						channel->playing->declick_stage = 0;
+						channel->playing->declick_volume = 1.f / 256.f;
+#endif
+					}
+				break;
+			case IT_TREMOLO:
+				{
+					unsigned char speed = entry->effectvalue >> 4;
+					unsigned char depth = entry->effectvalue & 15;
+					if (speed == 0)
+						speed = channel->lastRspeed;
+					channel->lastRspeed = speed;
+					if (depth == 0)
+						depth = channel->lastRdepth;
+					channel->lastRdepth = depth;
+					if (channel->playing) {
+						channel->playing->tremolo_speed = speed;
+						channel->playing->tremolo_depth = depth;
+					}
+				}
+				break;
+			case IT_S:
+				{
+					/* channel->lastS was set in update_pattern_variables(). */
+					unsigned char effectvalue = channel->lastS;
+					switch (effectvalue >> 4) {
+						//case IT_S_SET_FILTER:
+							/* Waveforms for commands S3x, S4x and S5x:
+							 *   0: Sine wave
+							 *   1: Ramp down
+							 *   2: Square wave
+							 *   3: Random wave
+							 */
+						case IT_S_SET_GLISSANDO_CONTROL:
+							channel->glissando = effectvalue & 15;
+							break;
+
+						case IT_S_FINETUNE:
+							if (channel->playing) {
+								channel->playing->finetune = ((int)(effectvalue & 15) - 8) << 5;
+							}
+							break;
+
+						case IT_S_SET_VIBRATO_WAVEFORM:
+							{
+								int waveform = effectvalue & 3;
+								if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform];
+								channel->vibrato_waveform = waveform;
+								if (channel->playing) {
+									channel->playing->vibrato_waveform = waveform;
+									if (!(effectvalue & 4))
+										channel->playing->vibrato_time = 0;
+								}
+							}
+							break;
+						case IT_S_SET_TREMOLO_WAVEFORM:
+							{
+								int waveform = effectvalue & 3;
+								if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform];
+								channel->tremolo_waveform = waveform;
+								if (channel->playing) {
+									channel->playing->tremolo_waveform = waveform;
+									if (!(effectvalue & 4))
+										channel->playing->tremolo_time = 0;
+								}
+							}
+							break;
+						case IT_S_SET_PANBRELLO_WAVEFORM:
+							channel->panbrello_waveform = effectvalue & 3;
+							if (channel->playing) {
+								channel->playing->panbrello_waveform = effectvalue & 3;
+								if (!(effectvalue & 4))
+									channel->playing->panbrello_time = 0;
+							}
+							break;
+
+						case IT_S_FINE_PATTERN_DELAY:
+							sigrenderer->tick += effectvalue & 15;
+							break;
+#if 1
+						case IT_S7:
+							{
+								if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS)
+								{
+									switch (effectvalue & 15)
+									{
+									case 3:
+										channel->new_note_action = NNA_NOTE_CUT;
+										break;
+									case 4:
+										channel->new_note_action = NNA_NOTE_CONTINUE;
+										break;
+									case 5:
+										channel->new_note_action = NNA_NOTE_OFF;
+										break;
+									case 6:
+										channel->new_note_action = NNA_NOTE_FADE;
+										break;
+
+									case 7:
+										if (channel->playing)
+											channel->playing->enabled_envelopes &= ~IT_ENV_VOLUME;
+										break;
+									case 8:
+										if (channel->playing)
+											channel->playing->enabled_envelopes |= IT_ENV_VOLUME;
+										break;
+
+									case 9:
+										if (channel->playing)
+											channel->playing->enabled_envelopes &= ~IT_ENV_PANNING;
+										break;
+									case 10:
+										if (channel->playing)
+											channel->playing->enabled_envelopes |= IT_ENV_PANNING;
+										break;
+
+									case 11:
+										if (channel->playing)
+											channel->playing->enabled_envelopes &= ~IT_ENV_PITCH;
+										break;
+									case 12:
+										if (channel->playing)
+											channel->playing->enabled_envelopes |= IT_ENV_PITCH;
+										break;
+									}
+								}
+							}
+							break;
+#endif
+						case IT_S_SET_PAN:
+							//ASSERT(!(sigdata->flags & IT_WAS_AN_XM));
+							channel->pan =
+								((effectvalue & 15) << 2) |
+								((effectvalue & 15) >> 2);
+							channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+
+							if (channel->playing)
+								channel->playing->panbrello_depth = 0;
+							break;
+						case IT_S_SET_SURROUND_SOUND:
+							if ((effectvalue & 15) == 1) {
+								channel->pan = IT_SURROUND;
+								channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+							}
+							if (channel->playing)
+								channel->playing->panbrello_depth = 0;
+							break;
+						case IT_S_SET_HIGH_OFFSET:
+							channel->high_offset = effectvalue & 15;
+							break;
+						//case IT_S_PATTERN_LOOP:
+						case IT_S_DELAYED_NOTE_CUT:
+							channel->note_cut_count = effectvalue & 15;
+							if (!channel->note_cut_count) {
+								if (sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM))
+									channel->volume = 0;
+								else
+									channel->note_cut_count = 1;
+							}
+							break;
+						case IT_S_SET_MIDI_MACRO:
+							channel->SFmacro = effectvalue & 15;
+							break;
+					}
+				}
+				break;
+			case IT_SET_SONG_TEMPO:
+				{
+					unsigned char v = entry->effectvalue;
+					if (v == 0)
+						v = channel->lastW;
+					channel->lastW = v;
+					if (v < 0x10)
+						sigrenderer->temposlide = -v;
+					else if (v < 0x20)
+						sigrenderer->temposlide = v & 15;
+					else
+						sigrenderer->tempo = v;
+				}
+				break;
+			case IT_FINE_VIBRATO:
+				{
+					unsigned char speed = entry->effectvalue >> 4;
+					unsigned char depth = entry->effectvalue & 15;
+					if (speed == 0)
+						speed = channel->lastHspeed;
+					channel->lastHspeed = speed;
+					if (depth == 0)
+						depth = channel->lastHdepth;
+					else {
+						if (sigdata->flags & IT_OLD_EFFECTS)
+							depth <<= 1;
+						channel->lastHdepth = depth;
+					}
+					if (channel->playing) {
+						channel->playing->vibrato_speed = speed;
+						channel->playing->vibrato_depth = depth;
+						channel->playing->vibrato_n++;
+					}
+				}
+				break;
+			case IT_SET_GLOBAL_VOLUME:
+				if (entry->effectvalue <= 128)
+					sigrenderer->globalvolume = entry->effectvalue;
+#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
+				else
+					sigrenderer->globalvolume = 128;
+#endif
+				break;
+			case IT_GLOBAL_VOLUME_SLIDE:
+				{
+					unsigned char v = entry->effectvalue;
+					if (v == 0)
+						v = channel->lastW;
+					channel->lastW = v;
+					if ((v & 0x0F) == 0) { /* Wx0 */
+						sigrenderer->globalvolslide =
+							(sigdata->flags & IT_WAS_AN_XM) ? (v >> 4)*2 : (v >> 4);
+					} else if ((v & 0xF0) == 0) { /* W0x */
+						sigrenderer->globalvolslide =
+							(sigdata->flags & IT_WAS_AN_XM) ? (-v)*2 : (-v);
+					} else if ((v & 0x0F) == 0x0F) { /* WxF */
+						sigrenderer->globalvolume += v >> 4;
+						if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 128;
+					} else if ((v & 0xF0) == 0xF0) { /* WFx */
+						sigrenderer->globalvolume -= v & 15;
+						if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 0;
+					}
+				}
+				break;
+			case IT_SET_PANNING:
+				if (sigdata->flags & IT_WAS_AN_XM) {
+					if (sigdata->flags & IT_WAS_A_MOD)
+						channel->truepan = entry->effectvalue*128;
+					else
+						channel->truepan = 32 + entry->effectvalue*64;
+				} else {
+					if (sigdata->flags & IT_WAS_AN_S3M)
+						channel->pan = (entry->effectvalue + 1) >> 1;
+					else
+						channel->pan = (entry->effectvalue + 2) >> 2;
+					channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+				}
+				if (channel->playing)
+					channel->playing->panbrello_depth = 0;
+				break;
+			case IT_PANBRELLO:
+				{
+					unsigned char speed = entry->effectvalue >> 4;
+					unsigned char depth = entry->effectvalue & 15;
+					if (speed == 0)
+						speed = channel->lastYspeed;
+					channel->lastYspeed = speed;
+					if (depth == 0)
+						depth = channel->lastYdepth;
+					channel->lastYdepth = depth;
+					if (channel->playing) {
+						channel->playing->panbrello_speed = speed;
+						channel->playing->panbrello_depth = depth;
+					}
+				}
+				break;
+			case IT_MIDI_MACRO:
+				{
+					const IT_MIDI *midi = sigdata->midi ? sigdata->midi : &default_midi;
+					if (entry->effectvalue >= 0x80) {
+						int n = midi->Zmacrolen[entry->effectvalue-0x80];
+						int i;
+						for (i = 0; i < n; i++)
+							it_send_midi(sigrenderer, channel, midi->Zmacro[entry->effectvalue-0x80][i]);
+					} else {
+						int n = midi->SFmacrolen[channel->SFmacro];
+						int i, j;
+						for (i = 0, j = 1; i < n; i++, j <<= 1)
+							it_send_midi(sigrenderer, channel,
+								midi->SFmacroz[channel->SFmacro] & j ?
+									entry->effectvalue : midi->SFmacro[channel->SFmacro][i]);
+					}
+				}
+				break;
+
+			/* uggly plain portamento for now */
+			case IT_PTM_NOTE_SLIDE_DOWN:
+			case IT_PTM_NOTE_SLIDE_DOWN_RETRIG:
+				{
+					channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_DOWN_RETRIG);
+
+					if (channel->ptm_last_toneslide) {
+						channel->toneslide_tick = channel->last_toneslide_tick;
+
+						if (--channel->toneslide_tick == 0) {
+							channel->truenote += channel->toneslide;
+							if (channel->truenote >= 120) {
+								if (channel->toneslide < 0) channel->truenote = 0;
+								else channel->truenote = 119;
+							}
+							channel->note += channel->toneslide;
+							if (channel->note >= 120) {
+								if (channel->toneslide < 0) channel->note = 0;
+								else channel->note = 119;
+							}
+
+							if (channel->playing) {
+								if (channel->sample) channel->playing->note = channel->truenote;
+								else channel->playing->note = channel->note;
+								it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+								channel->playing->declick_stage = 0;
+								channel->playing->declick_volume = 1.f / 256.f;
+#endif
+							}
+						}
+					}
+
+					channel->ptm_last_toneslide = 0;
+
+					channel->toneslide = -(entry->effectvalue & 15);
+					channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4;
+					channel->toneslide_tick += channel->ptm_toneslide;
+				}
+				break;
+			case IT_PTM_NOTE_SLIDE_UP:
+			case IT_PTM_NOTE_SLIDE_UP_RETRIG:
+				{
+					channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_UP_RETRIG);
+
+					if (channel->ptm_last_toneslide) {
+						channel->toneslide_tick = channel->last_toneslide_tick;
+
+						if (--channel->toneslide_tick == 0) {
+							channel->truenote += channel->toneslide;
+							if (channel->truenote >= 120) {
+								if (channel->toneslide < 0) channel->truenote = 0;
+								else channel->truenote = 119;
+							}
+							channel->note += channel->toneslide;
+							if (channel->note >= 120) {
+								if (channel->toneslide < 0) channel->note = 0;
+								else channel->note = 119;
+							}
+
+							if (channel->playing) {
+								if (channel->sample) channel->playing->note = channel->truenote;
+								else channel->playing->note = channel->note;
+								it_playing_reset_resamplers(channel->playing, 0);
+#ifdef END_RAMPING
+								channel->playing->declick_stage = 0;
+								channel->playing->declick_volume = 1.f / 256.f;
+#endif
+							}
+						}
+					}
+
+					channel->ptm_last_toneslide = 0;
+
+					channel->toneslide = -(entry->effectvalue & 15);
+					channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4;
+					channel->toneslide_tick += channel->ptm_toneslide;
+				}
+				break;
+		}
+	}
+
+	if (!(sigdata->flags & IT_WAS_AN_XM))
+		post_process_it_volpan(sigrenderer, entry);
+
+	return 0;
+}
+
+
+
+static int process_it_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+	// When tone portamento and instrument are specified:
+	// If Gxx is off:
+	//   - same sample, do nothing but portamento
+	//   - diff sample, retrigger all but keep current note+slide + do porta
+	//   - if instrument is invalid, nothing; if sample is invalid, cut
+	// If Gxx is on:
+	//   - same sample or new sample invalid, retrigger envelopes and initialise note value for portamento to 'seek' to
+	//   - diff sample/inst, start using new envelopes
+	// When tone portamento is specified alone, sample won't change.
+	// TODO: consider what happens with instrument alone after all this...
+
+	if (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) {
+		if (entry->mask & IT_ENTRY_INSTRUMENT)
+			channel->instrument = entry->instrument;
+		instrument_to_sample(sigdata, channel);
+		if (channel->note < 120) {
+			if ((sigdata->flags & IT_USE_INSTRUMENTS) && channel->sample == 0)
+				return 1;
+			if (entry->mask & IT_ENTRY_INSTRUMENT)
+				get_default_volpan(sigdata, channel);
+		} else
+			it_retrigger_note(sigrenderer, channel); /* Stop the note */
+	}
+
+	/** WARNING: This is not ideal, since channel->playing might not get allocated owing to lack of memory... */
+	if (((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) ||
+	    ((entry->mask & IT_ENTRY_EFFECT) && (entry->effect == IT_TONE_PORTAMENTO || entry->effect == IT_VOLSLIDE_TONEPORTA)))
+	{
+		if (channel->playing && (entry->mask & IT_ENTRY_INSTRUMENT)) {
+			if (sigdata->flags & IT_COMPATIBLE_GXX)
+				it_compatible_gxx_retrigger(sigdata, channel);
+			else if ((!(sigdata->flags & IT_USE_INSTRUMENTS) ||
+				(channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments)) &&
+				channel->sample != channel->playing->sampnum)
+			{
+				unsigned char note = channel->playing->note;
+				int slide = channel->playing->slide;
+				it_retrigger_note(sigrenderer, channel);
+				if (channel->playing) {
+					channel->playing->note = note;
+					channel->playing->slide = slide;
+					// Should we be preserving sample_vibrato_time? depth?
+				}
+			}
+		}
+
+		if ((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) {
+			/* Tone Portamento in the volume column */
+			static const unsigned char slidetable[] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255};
+			unsigned char v = slidetable[entry->volpan - 193];
+			if (sigdata->flags & IT_COMPATIBLE_GXX) {
+				if (v == 0)
+					v = channel->lastG;
+				channel->lastG = v;
+			} else {
+				if (v == 0)
+					v = channel->lastEF;
+				channel->lastEF = v;
+			}
+			if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) {
+				if (channel->note < 120) {
+					if (channel->sample)
+						channel->destnote = channel->truenote;
+					else
+						channel->destnote = channel->note;
+				}
+			}
+			channel->toneporta = v << 4;
+		} else {
+			/* Tone Portamento in the effect column */
+			unsigned char v;
+			if (entry->effect == IT_TONE_PORTAMENTO)
+				v = entry->effectvalue;
+			else
+				v = 0;
+			if (sigdata->flags & IT_COMPATIBLE_GXX) {
+				if (v == 0)
+					v = channel->lastG;
+				channel->lastG = v;
+			} else {
+				if (v == 0)
+					v = channel->lastEF;
+				channel->lastEF = v;
+			}
+			if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) {
+				if (channel->note < 120) {
+					if (channel->sample)
+						channel->destnote = channel->truenote;
+					else
+						channel->destnote = channel->note;
+				}
+			}
+			channel->toneporta = v << 4;
+		}
+		if (channel->playing) goto skip_start_note;
+	}
+
+	if ((entry->mask & IT_ENTRY_NOTE) ||
+		((entry->mask & IT_ENTRY_INSTRUMENT) && (!channel->playing || entry->instrument != channel->playing->instnum)))
+	{
+		if (channel->note < 120) {
+			get_true_pan(sigdata, channel);
+			if ((entry->mask & IT_ENTRY_NOTE) || !(sigdata->flags & (IT_WAS_AN_S3M|IT_WAS_A_PTM)))
+				it_retrigger_note(sigrenderer, channel);
+		}
+	}
+
+	skip_start_note:
+
+	if (entry->mask & IT_ENTRY_VOLPAN) {
+		if (entry->volpan <= 64) {
+			/* Volume */
+			channel->volume = entry->volpan;
+		} else if (entry->volpan <= 74) {
+			/* Fine volume slide up */
+			unsigned char v = entry->volpan - 65;
+			if (v == 0)
+				v = channel->lastvolslide;
+			channel->lastvolslide = v;
+			/* = effect DxF where x == entry->volpan - 65 */
+			channel->volume += v;
+			if (channel->volume > 64) channel->volume = 64;
+		} else if (entry->volpan <= 84) {
+			/* Fine volume slide down */
+			unsigned char v = entry->volpan - 75;
+			if (v == 0)
+				v = channel->lastvolslide;
+			channel->lastvolslide = v;
+			/* = effect DFx where x == entry->volpan - 75 */
+			channel->volume -= v;
+			if (channel->volume > 64) channel->volume = 0;
+		} else if (entry->volpan < 128) {
+			/* Volume slide up */
+			/* Volume slide down */
+			/* Portamento down */
+			/* Portamento up */
+		} else if (entry->volpan <= 192) {
+			/* Pan */
+			channel->pan = entry->volpan - 128;
+			channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+		}
+		/* else */
+		/* Tone Portamento */
+		/* Vibrato */
+	}
+	return 0;
+}
+
+
+
+static void retrigger_xm_envelopes(IT_PLAYING *playing)
+{
+	playing->volume_envelope.next_node = 0;
+	playing->volume_envelope.tick = -1;
+	playing->pan_envelope.next_node = 0;
+	playing->pan_envelope.tick = -1;
+	playing->fadeoutcount = 1024;
+}
+
+
+
+static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry)
+{
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+	IT_PLAYING playing;
+
+	playing.sample = 0;
+
+	if (entry->mask & IT_ENTRY_INSTRUMENT) {
+		int oldsample = channel->sample;
+		int oldvolume = channel->volume;
+		channel->instrument = entry->instrument;
+		instrument_to_sample(sigdata, channel);
+		if (channel->playing &&
+			!((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) &&
+			!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0)) {
+			playing = *channel->playing;
+			if (!(sigdata->flags & IT_WAS_A_MOD)) {
+				/* Retrigger vol/pan envelopes if enabled, and cancel fadeout.
+				 * Also reset vol/pan to that of _original_ instrument.
+				 */
+				channel->playing->flags &= ~(IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING);
+				it_playing_update_resamplers(channel->playing);
+
+				channel->volume = channel->playing->sample->default_volume;
+				channel->truepan = 32 + channel->playing->sample->default_pan*64;
+
+				retrigger_xm_envelopes(channel->playing);
+			} else {
+				/* Switch if sample changed */
+				if (oldsample != channel->sample) {
+#ifdef RAMP_DOWN
+					int i;
+					for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+						if (!sigrenderer->playing[i]) {
+							channel->playing->declick_stage = 2;
+							sigrenderer->playing[i] = channel->playing;
+							channel->playing = NULL;
+							break;
+						}
+					}
+
+					if (!channel->sample) {
+						if (channel->playing)
+						{
+							free(channel->playing);
+							channel->playing = NULL;
+						}
+					} else {
+						if (!channel->playing) {
+							channel->playing = malloc(sizeof(*channel->playing));
+							if (!channel->playing) return;
+						}
+						*channel->playing = playing;
+						playing.sample = (IT_SAMPLE *)-1;
+						channel->playing->declick_stage = 0;
+						channel->playing->declick_volume = 1.f / 256.f;
+#else
+					if (!channel->sample) {
+						free(channel->playing);
+						channel->playing = NULL;
+					} else {
+#endif
+						channel->playing->sampnum = channel->sample;
+						channel->playing->sample = &sigdata->sample[channel->sample-1];
+						it_playing_reset_resamplers(channel->playing, 0);
+					}
+				}
+				get_default_volpan(sigdata, channel);
+			}
+#ifdef END_RAMPING
+			if (channel->volume && !oldvolume) {
+				channel->playing->declick_stage = 0;
+				channel->playing->declick_volume = 1.f / 256.f;
+			}
+#endif
+		}
+	}
+
+	if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) &&
+		(entry->mask & IT_ENTRY_NOTE))
+	{
+		if (!(entry->mask & IT_ENTRY_INSTRUMENT))
+			instrument_to_sample(sigdata, channel);
+
+		if (channel->note >= 120)
+			xm_note_off(sigdata, channel);
+		else if (channel->sample == 0) {
+			/** If we get here, one of the following is the case:
+			 ** 1. The instrument has never been specified on this channel.
+			 ** 2. The specified instrument is invalid.
+			 ** 3. The instrument has no sample mapped to the selected note.
+			 ** What should happen?
+			 **
+			 ** Experimentation shows that any existing note stops and cannot
+			 ** be brought back. A subsequent instrument change fixes that.
+			 **/
+			if (channel->playing) {
+#ifdef RAMP_DOWN
+				int i;
+				if (playing.sample) *channel->playing = playing;
+				for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+					if (!sigrenderer->playing[i]) {
+						channel->playing->declick_stage = 2;
+						sigrenderer->playing[i] = channel->playing;
+						channel->playing = NULL;
+						break;
+					}
+				}
+				if (channel->playing) {
+					free(channel->playing);
+					channel->playing = NULL;
+				}
+#else
+				free(channel->playing);
+				channel->playing = NULL;
+#endif
+			}
+			return;
+		} else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) {
+			/* Don't retrigger note; portamento in the volume column. */
+		} else if (channel->playing &&
+		           (entry->mask & IT_ENTRY_EFFECT) &&
+		           (entry->effect == IT_TONE_PORTAMENTO ||
+		            entry->effect == IT_VOLSLIDE_TONEPORTA)) {
+			/* Don't retrigger note; portamento in the effects column. */
+		} else {
+			channel->destnote = IT_NOTE_OFF;
+
+			if (!channel->playing) {
+				channel->playing = malloc(sizeof(*channel->playing));
+				if (!channel->playing)
+					return;
+				// Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone.
+				retrigger_xm_envelopes(channel->playing);
+			}
+#ifdef RAMP_DOWN
+			else if (playing.sample != (IT_SAMPLE *)-1) {
+				/* volume rampy stuff! move note to NNA */
+				int i;
+				IT_PLAYING * ptemp = malloc(sizeof(*channel->playing));
+				if (!ptemp) return;
+				if (playing.sample) *ptemp = playing;
+				else *ptemp = *channel->playing;
+				for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+					if (!sigrenderer->playing[i]) {
+						ptemp->declick_stage = 2;
+						ptemp->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING;
+						sigrenderer->playing[i] = ptemp;
+						ptemp = NULL;
+						break;
+					}
+				}
+				if (ptemp) free(ptemp);
+			}
+#endif
+
+			channel->playing->flags = 0;
+			channel->playing->resampling_quality = sigrenderer->resampling_quality;
+			channel->playing->channel = channel;
+			channel->playing->sample = &sigdata->sample[channel->sample-1];
+			if (sigdata->flags & IT_USE_INSTRUMENTS)
+				channel->playing->instrument = &sigdata->instrument[channel->instrument-1];
+			else
+				channel->playing->instrument = NULL;
+			channel->playing->env_instrument = channel->playing->instrument;
+			channel->playing->sampnum = channel->sample;
+			channel->playing->instnum = channel->instrument;
+#ifdef END_RAMPING
+			channel->playing->declick_stage = 0;
+			channel->playing->declick_volume = 1.f / 256.f;
+#endif
+			channel->playing->channel_volume = channel->channelvolume;
+			channel->playing->ramp_volume[0] = 31337.0; /* special */
+			channel->playing->note = channel->truenote;
+			channel->playing->enabled_envelopes = 0;
+			channel->playing->volume_offset = 0;
+			channel->playing->panning_offset = 0;
+			if (sigdata->flags & IT_USE_INSTRUMENTS) {
+				IT_PLAYING * playing = channel->playing;
+				IT_INSTRUMENT * instrument = playing->instrument;
+				if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME;
+				if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING;
+			}
+			channel->playing->filter_cutoff = 127;
+			channel->playing->filter_resonance = 0;
+			channel->playing->true_filter_cutoff = 127 << 8;
+			channel->playing->true_filter_resonance = 0;
+			channel->playing->vibrato_speed = 0;
+			channel->playing->vibrato_depth = 0;
+			channel->playing->vibrato_n = 0;
+			channel->playing->vibrato_time = 0;
+			channel->playing->vibrato_waveform = 0;
+			channel->playing->tremolo_speed = 0;
+			channel->playing->tremolo_depth = 0;
+			channel->playing->tremolo_time = 0;
+			channel->playing->tremolo_waveform = 0;
+			channel->playing->panbrello_speed = 0;
+			channel->playing->panbrello_depth = 0;
+			channel->playing->panbrello_time = 0;
+			channel->playing->panbrello_waveform = 0;
+			channel->playing->panbrello_random = 0;
+			channel->playing->sample_vibrato_time = 0;
+			channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform;
+			channel->playing->sample_vibrato_depth = 0;
+			channel->playing->slide = 0;
+			channel->playing->finetune = 0;
+			it_reset_filter_state(&channel->playing->filter_state[0]); // Are these
+			it_reset_filter_state(&channel->playing->filter_state[1]); // necessary?
+			it_playing_reset_resamplers(channel->playing, 0);
+
+			/** WARNING - is everything initialised? */
+		}
+	}
+
+	if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) &&
+		!((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) &&
+		(entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) == (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT))
+	{
+		if (channel->playing) retrigger_xm_envelopes(channel->playing);
+		get_default_volpan(sigdata, channel);
+	}
+
+	if ((entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) {
+		/* Tone Portamento */
+		unsigned char v = (entry->volpan & 15) << 4;
+		if (v == 0)
+			v = channel->lastG;
+		channel->lastG = v;
+		if (entry->mask & IT_ENTRY_NOTE)
+			if (channel->sample && channel->note < 120)
+				channel->destnote = channel->truenote;
+		channel->toneporta = v << 4;
+	} else if ((entry->mask & IT_ENTRY_EFFECT) &&
+	           (entry->effect == IT_TONE_PORTAMENTO ||
+	            entry->effect == IT_VOLSLIDE_TONEPORTA)) {
+		unsigned char v;
+		if (entry->effect == IT_TONE_PORTAMENTO)
+			v = entry->effectvalue;
+		else
+			v = 0;
+		if (v == 0)
+			v = channel->lastG;
+		channel->lastG = v;
+		if (entry->mask & IT_ENTRY_NOTE)
+			if (channel->sample && channel->note < 120)
+				channel->destnote = channel->truenote;
+		channel->toneporta = v << 4;
+	}
+
+	if (entry->mask & IT_ENTRY_VOLPAN) {
+		int effect = entry->volpan >> 4;
+		int value  = entry->volpan & 15;
+		switch (effect) {
+			case 0x6: /* Volume slide down */
+				channel->xm_volslide = -value;
+				break;
+			case 0x7: /* Volume slide up */
+				channel->xm_volslide = value;
+				break;
+			case 0x8: /* Fine volume slide down */
+				channel->volume -= value;
+				if (channel->volume > 64) channel->volume = 0;
+				break;
+			case 0x9: /* Fine volume slide up */
+				channel->volume += value;
+				if (channel->volume > 64) channel->volume = 64;
+				break;
+			case 0xA: /* Set vibrato speed */
+				if (value)
+					channel->lastHspeed = value;
+				if (channel->playing)
+					channel->playing->vibrato_speed = channel->lastHspeed;
+				break;
+			case 0xB: /* Vibrato */
+				if (value)
+					channel->lastHdepth = value << 2; /** WARNING: correct ? */
+				if (channel->playing) {
+					channel->playing->vibrato_depth = channel->lastHdepth;
+					channel->playing->vibrato_speed = channel->lastHspeed;
+					channel->playing->vibrato_n++;
+				}
+				break;
+			case 0xC: /* Set panning */
+				channel->truepan = 32 + value*(17*64);
+				break;
+			case 0xD: /* Pan slide left */
+				/* -128 is a special case for emulating a 'feature' in FT2.
+				 * As soon as effects are processed, it goes hard left.
+				 */
+				channel->panslide = value ? -value : -128;
+				break;
+			case 0xE: /* Pan slide Right */
+				channel->panslide = value;
+				break;
+			case 0xF: /* Tone porta */
+				break;
+			default:  /* Volume */
+				channel->volume = entry->volpan - 0x10;
+				break;
+		}
+	}
+}
+
+
+
+/* This function assumes !IT_IS_END_ROW(entry). */
+static int process_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx)
+{
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+
+	if (sigdata->flags & IT_WAS_AN_XM)
+		process_xm_note_data(sigrenderer, entry);
+	else
+		if (process_it_note_data(sigrenderer, entry)) return 0;
+
+	return process_effects(sigrenderer, entry, ignore_cxx);
+}
+
+
+
+static int process_entry(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx)
+{
+	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
+
+	if (entry->mask & IT_ENTRY_NOTE)
+		channel->note = entry->note;
+
+	if ((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_S) {
+		/* channel->lastS was set in update_pattern_variables(). */
+		unsigned char effectvalue = channel->lastS;
+		if (effectvalue >> 4 == IT_S_NOTE_DELAY) {
+			channel->note_delay_count = effectvalue & 15;
+			if (channel->note_delay_count == 0)
+				channel->note_delay_count = 1;
+			channel->note_delay_entry = entry;
+			return 0;
+		}
+	}
+
+	return process_note_data(sigrenderer, entry, ignore_cxx);
+}
+
+
+
+static void update_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	int i;
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+
+		if (channel->key_off_count) {
+			channel->key_off_count--;
+			if (channel->key_off_count == 0)
+				xm_note_off(sigrenderer->sigdata, channel);
+		} else if (channel->note_cut_count) {
+			channel->note_cut_count--;
+			if (channel->note_cut_count == 0) {
+				if (sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM))
+					channel->volume = 0;
+				else if (channel->playing) {
+#ifdef RAMP_DOWN
+					int i;
+					for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+						if (!sigrenderer->playing[i]) {
+							channel->playing->declick_stage = 2;
+							sigrenderer->playing[i] = channel->playing;
+							channel->playing = NULL;
+							break;
+						}
+					}
+					if (channel->playing) {
+						free(channel->playing);
+						channel->playing = NULL;
+					}
+#else
+					free(channel->playing);
+					channel->playing = NULL;
+#endif
+				}
+			}
+		} else if (channel->note_delay_count) {
+			channel->note_delay_count--;
+			if (channel->note_delay_count == 0)
+				process_note_data(sigrenderer, channel->note_delay_entry, 0);
+					/* Don't bother checking the return value; if the note
+					 * was delayed, there can't have been a speed=0.
+					 */
+		}
+	}
+}
+
+
+
+static int envelope_get_y(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+#if 1
+	return pe->value;
+#else
+	int ys, ye;
+	int ts, te;
+	int t;
+
+	if (pe->next_node <= 0)
+		return envelope->node_y[0] << IT_ENVELOPE_SHIFT;
+
+	if (pe->next_node >= envelope->n_nodes)
+		return envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT;
+
+	ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT;
+	ts = envelope->node_t[pe->next_node-1];
+	te = envelope->node_t[pe->next_node];
+
+	if (ts == te)
+		return ys;
+
+	ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT;
+
+	t = pe->tick;
+
+	return ys + (ye - ys) * (t - ts) / (te - ts);
+#endif
+}
+
+
+
+#if 0
+static int it_envelope_end(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+	if (pe->next_node >= envelope->n_nodes)
+		return 1;
+
+	if (pe->tick < envelope->node_t[pe->next_node]) return 0;
+
+	if ((envelope->flags & IT_ENVELOPE_LOOP_ON) &&
+	    envelope->loop_end >= pe->next_node &&
+	    envelope->node_t[envelope->loop_end] <= pe->tick) return 0;
+
+	if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) &&
+	    !(playing->flags & IT_PLAYING_SUSTAINOFF) &&
+	    envelope->sus_loop_end >= pe->next_node &&
+	    envelope->node_t[envelope->sus_loop_end] <= pe->tick) return 0;
+
+	if (envelope->node_t[envelope->n_nodes-1] <= pe->tick) return 1;
+
+	return 0;
+}
+#endif
+
+
+
+/* Returns 1 when fading should be initiated for a volume envelope. */
+static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe, int flags)
+{
+	if (!(playing->enabled_envelopes & flags))
+		return 0;
+
+	ASSERT(envelope->n_nodes > 0);
+
+	if (pe->next_node <= 0)
+		pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT;
+	else if (pe->next_node >= envelope->n_nodes) {
+		pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT;
+		return 1;
+	} else {
+		int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT;
+		int ts = envelope->node_t[pe->next_node-1];
+		int te = envelope->node_t[pe->next_node];
+
+		if (ts == te)
+			pe->value = ys;
+		else {
+			int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT;
+			int t = pe->tick;
+
+			pe->value = ys + (ye - ys) * (t - ts) / (te - ts);
+		}
+	}
+
+	pe->tick++;
+	while (pe->tick >= envelope->node_t[pe->next_node]) {
+		pe->next_node++;
+		if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) {
+			if (pe->next_node > envelope->sus_loop_end) {
+				pe->next_node = envelope->sus_loop_start;
+				ASSERT(pe->next_node < envelope->n_nodes);
+				pe->tick = envelope->node_t[envelope->sus_loop_start];
+				return 0;
+			}
+		} else if (envelope->flags & IT_ENVELOPE_LOOP_ON) {
+			if (pe->next_node > envelope->loop_end) {
+				pe->next_node = envelope->loop_start;
+				ASSERT(pe->next_node < envelope->n_nodes);
+				pe->tick = envelope->node_t[envelope->loop_start];
+				return 0;
+			}
+		}
+		if (pe->next_node >= envelope->n_nodes)
+			return 0;
+	}
+	return 0;
+}
+
+
+
+static void update_it_envelopes(IT_PLAYING *playing)
+{
+	IT_ENVELOPE *envelope = &playing->env_instrument->volume_envelope;
+	IT_PLAYING_ENVELOPE *pe = &playing->volume_envelope;
+
+	if (update_it_envelope(playing, envelope, pe, IT_ENV_VOLUME)) {
+		playing->flags |= IT_PLAYING_FADING;
+		if (pe->value == 0)
+			playing->flags |= IT_PLAYING_DEAD;
+	}
+
+	update_it_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope, IT_ENV_PANNING);
+	update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope, IT_ENV_PITCH);
+}
+
+
+
+static int xm_envelope_is_sustaining(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+	if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF))
+		if (envelope->sus_loop_start < envelope->n_nodes)
+			if (pe->tick == envelope->node_t[envelope->sus_loop_start])
+				return 1;
+	return 0;
+}
+
+
+
+static void update_xm_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe)
+{
+	if (!(envelope->flags & IT_ENVELOPE_ON))
+		return;
+
+	if (xm_envelope_is_sustaining(playing, envelope, pe))
+		return;
+
+	if (pe->tick >= envelope->node_t[envelope->n_nodes-1])
+		return;
+
+	pe->tick++;
+
+	/* pe->next_node must be kept up to date for envelope_get_y(). */
+	while (pe->tick > envelope->node_t[pe->next_node])
+		pe->next_node++;
+
+	if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && envelope->loop_end < envelope->n_nodes) {
+		if (pe->tick == envelope->node_t[envelope->loop_end]) {
+			pe->next_node = MID(0, envelope->loop_start, envelope->n_nodes - 1);
+			pe->tick = envelope->node_t[pe->next_node];
+		}
+	}
+
+	if (pe->next_node <= 0)
+		pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT;
+	else if (pe->next_node >= envelope->n_nodes)
+		pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT;
+	else {
+		int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT;
+		int ts = envelope->node_t[pe->next_node-1];
+		int te = envelope->node_t[pe->next_node];
+
+		if (ts == te)
+			pe->value = ys;
+		else {
+			int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT;
+			int t = pe->tick;
+
+			pe->value = ys + (ye - ys) * (t - ts) / (te - ts);
+		}
+	}
+}
+
+
+
+static void update_xm_envelopes(IT_PLAYING *playing)
+{
+	update_xm_envelope(playing, &playing->env_instrument->volume_envelope, &playing->volume_envelope);
+	update_xm_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope);
+}
+
+
+
+static void update_fadeout(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing)
+{
+	if (playing->flags & IT_PLAYING_FADING) {
+		playing->fadeoutcount -= playing->env_instrument->fadeout;
+		if (playing->fadeoutcount <= 0) {
+			playing->fadeoutcount = 0;
+			if (!(sigdata->flags & IT_WAS_AN_XM))
+				playing->flags |= IT_PLAYING_DEAD;
+		}
+	}
+}
+
+static int apply_pan_envelope(IT_PLAYING *playing);
+static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume);
+
+static void playing_volume_setup(DUMB_IT_SIGRENDERER * sigrenderer, IT_PLAYING * playing, float invt2g)
+{
+	DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata;
+	int pan;
+	float volume, vol, span;
+
+	if ((sigrenderer->n_channels == 2) && (sigdata->flags & IT_STEREO)) {
+		pan = apply_pan_envelope(playing);
+		if ((sigdata->flags & IT_WAS_AN_S3M) && (pan > (64<<8))) {
+			volume = -1.0f * (float)(pan) * (1.0f / (64.0f * 256.0f));
+		} else {
+			volume = 1.0f;
+		}
+		span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128));
+		vol = volume;
+		if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 1.0f - span;
+		playing->float_volume[0] = vol;
+		vol = -vol;
+		if (!IT_IS_SURROUND_SHIFTED(pan)) vol += 2.0f * volume;
+		playing->float_volume[1] = vol;
+	} else {
+		if ((sigdata->flags & IT_STEREO) && (playing->sample->flags & IT_SAMPLE_STEREO)) {
+			pan = apply_pan_envelope(playing);
+			span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128));
+			vol = 0.5f;
+			if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 1.0f - span;
+			playing->float_volume[0] = vol;
+			if (!IT_IS_SURROUND_SHIFTED(pan)) vol = 2.0f - vol;
+			playing->float_volume[1] = vol;
+		} else {
+			playing->float_volume[0] = 1.0f;
+			playing->float_volume[1] = 1.0f;
+		}
+	}
+
+	vol = calculate_volume(sigrenderer, playing, 1.0f);
+	playing->float_volume[0] *= vol;
+	playing->float_volume[1] *= vol;
+
+	if (!sigrenderer->ramp_style || playing->ramp_volume[0] == 31337.0) {
+		playing->ramp_volume[0] = playing->float_volume[0];
+		playing->ramp_volume[1] = playing->float_volume[1];
+		playing->ramp_delta[0] = 0;
+		playing->ramp_delta[1] = 0;
+	} else {
+		playing->ramp_delta[0] = invt2g * (playing->float_volume[0] - playing->ramp_volume[0]);
+		playing->ramp_delta[1] = invt2g * (playing->float_volume[1] - playing->ramp_volume[1]);
+	}
+}
+
+static void process_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float invt2g)
+{
+	DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata;
+
+	if (playing->instrument) {
+		if (sigdata->flags & IT_WAS_AN_XM)
+			update_xm_envelopes(playing);
+		else
+			update_it_envelopes(playing);
+		update_fadeout(sigdata, playing);
+	}
+
+	playing_volume_setup(sigrenderer, playing, invt2g);
+
+	if (sigdata->flags & IT_WAS_AN_XM) {
+		/* 'depth' is used to store the tick number for XM files. */
+		if (playing->sample_vibrato_depth < playing->sample->vibrato_rate)
+			playing->sample_vibrato_depth++;
+	} else {
+		playing->sample_vibrato_depth += playing->sample->vibrato_rate;
+		if (playing->sample_vibrato_depth > playing->sample->vibrato_depth << 8)
+			playing->sample_vibrato_depth = playing->sample->vibrato_depth << 8;
+	}
+
+	playing->sample_vibrato_time += playing->sample->vibrato_speed;
+}
+
+static float log2(float x) {return (float)log(x)/(float)log(2.0f);}
+
+static int delta_to_note(float delta, int base)
+{
+	float note;
+	note = log2(delta * 65536.f / (float)base)*12.0f+60.5f;
+	if (note > 119) note = 119;
+	else if (note < 0) note = 0;
+	return (int)note;
+}
+
+static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+	int i;
+
+	float invt2g = 1.0f / ((float)TICK_TIME_DIVIDEND / (float)sigrenderer->tempo);
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+		IT_PLAYING *playing = channel->playing;
+
+		if (playing) {
+			int vibrato_shift;
+			switch (playing->vibrato_waveform)
+			{
+			default:
+				vibrato_shift = it_sine[playing->vibrato_time];
+				break;
+			case 1:
+				vibrato_shift = it_sawtooth[playing->vibrato_time];
+				break;
+			case 2:
+				vibrato_shift = it_squarewave[playing->vibrato_time];
+				break;
+			case 3:
+				vibrato_shift = (rand() % 129) - 64;
+				break;
+			case 4:
+				vibrato_shift = it_xm_squarewave[playing->vibrato_time];
+				break;
+			case 5:
+				vibrato_shift = it_xm_ramp[playing->vibrato_time];
+				break;
+			case 6:
+				vibrato_shift = it_xm_ramp[255-playing->vibrato_time];
+				break;
+			}
+			vibrato_shift *= playing->vibrato_n;
+			vibrato_shift *= playing->vibrato_depth;
+			vibrato_shift >>= 4;
+
+			if (sigdata->flags & IT_OLD_EFFECTS)
+				vibrato_shift = -vibrato_shift;
+
+			playing->volume = channel->volume;
+			playing->pan = channel->truepan;
+
+			if (playing->volume_offset) {
+				playing->volume += (playing->volume_offset * playing->volume) >> 7;
+				if (playing->volume > 64) {
+					if (playing->volume_offset < 0) playing->volume = 0;
+					else playing->volume = 64;
+				}
+			}
+
+			if (playing->panning_offset && !IT_IS_SURROUND_SHIFTED(playing->pan)) {
+				playing->pan += playing->panning_offset << IT_ENVELOPE_SHIFT;
+				if (playing->pan > 64 << IT_ENVELOPE_SHIFT) {
+					if (playing->panning_offset < 0) playing->pan = 0;
+					else playing->pan = 64 << IT_ENVELOPE_SHIFT;
+				}
+			}
+
+			if (sigdata->flags & IT_LINEAR_SLIDES) {
+				int currpitch = ((playing->note - 60) << 8) + playing->slide
+				                                            + vibrato_shift;
+
+				/* We add a feature here, which is that of keeping the pitch
+				 * within range. Otherwise it crashes. Trust me. It happened.
+				 * The limit 32768 gives almost 11 octaves either way.
+				 */
+				if (currpitch < -32768)
+					currpitch = -32768;
+				else if (currpitch > 32767)
+					currpitch = 32767;
+
+				playing->delta = (float)pow(DUMB_PITCH_BASE, currpitch);
+				playing->delta *= playing->sample->C5_speed * (1.f / 65536.0f);
+			} else {
+				int slide = playing->slide + vibrato_shift;
+
+				playing->delta = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note);
+				/* playing->delta is 1.0 for C-5, 0.5 for C-6, etc. */
+
+				playing->delta *= 1.0f / playing->sample->C5_speed;
+
+				playing->delta -= slide / AMIGA_DIVISOR;
+
+				if (playing->delta < (1.0f / 65536.0f) / 32768.0f) {
+					// Should XM notes die if Amiga slides go out of range?
+					playing->flags |= IT_PLAYING_DEAD;
+					playing->delta = 1. / 32768.;
+					continue;
+				}
+
+				playing->delta = (1.0f / 65536.0f) / playing->delta;
+			}
+
+			if (playing->channel->glissando && playing->channel->toneporta && playing->channel->destnote < 120) {
+				playing->delta = (float)pow(DUMB_SEMITONE_BASE, delta_to_note(playing->delta, playing->sample->C5_speed) - 60)
+					* playing->sample->C5_speed * (1.f / 65536.f);
+			}
+
+			playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio >> 8);
+
+			if (playing->finetune)
+				playing->delta *= (float)pow(DUMB_PITCH_BASE, playing->finetune);
+
+			playing->filter_cutoff = channel->filter_cutoff;
+			playing->filter_resonance = channel->filter_resonance;
+		}
+	}
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		if (sigrenderer->channel[i].playing) {
+			process_playing(sigrenderer, sigrenderer->channel[i].playing, invt2g);
+			if (!(sigdata->flags & IT_WAS_AN_XM)) {
+				//if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) {
+				// This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it.
+				if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) {
+					free(sigrenderer->channel[i].playing);
+					sigrenderer->channel[i].playing = NULL;
+				}
+			}
+		}
+	}
+
+	for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+		if (sigrenderer->playing[i]) {
+			process_playing(sigrenderer, sigrenderer->playing[i], invt2g);
+			if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) {
+				free(sigrenderer->playing[i]);
+				sigrenderer->playing[i] = NULL;
+			}
+		}
+	}
+}
+
+
+
+static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer)
+{
+	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
+
+	// Set note vol/freq to vol/freq set for each channel
+
+	if (sigrenderer->speed && --sigrenderer->tick == 0) {
+		reset_tick_counts(sigrenderer);
+		sigrenderer->tick = sigrenderer->speed;
+		sigrenderer->rowcount--;
+		if (sigrenderer->rowcount == 0) {
+			sigrenderer->rowcount = 1;
+
+#ifdef BIT_ARRAY_BULLSHIT
+			if (sigrenderer->n_rows)
+			{
+#if 1
+				/*
+				if (bit_array_test(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row))
+				{
+					if (sigrenderer->callbacks->loop) {
+						if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data))
+							return 1;
+						bit_array_reset(sigrenderer->played);
+						if (sigrenderer->speed == 0)
+							goto speed0; /* I love goto *
+					}
+				}
+				*/
+#endif
+				bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row);
+				{
+					int n;
+					for (n = 0; n < DUMB_IT_N_CHANNELS; n++)
+					{
+						IT_CHANNEL * channel = &sigrenderer->channel[n];
+						if (channel->played_patjump)
+						{
+							if (channel->played_patjump_order == sigrenderer->order)
+							{
+								bit_array_set(channel->played_patjump, sigrenderer->row);
+							}
+							/*
+							else if ((channel->played_patjump_order & 0x7FFF) == sigrenderer->order)
+							{
+								channel->played_patjump_order |= 0x4000;
+							}
+							else if ((channel->played_patjump_order & 0x3FFF) == sigrenderer->order)
+							{
+								if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM)
+								{
+									/* joy, was XM, pattern loop bug triggered break to row in same order 
+									bit_array_mask(sigrenderer->played, channel->played_patjump, sigrenderer->order * 256);
+								}
+								bit_array_destroy(channel->played_patjump);
+								channel->played_patjump = 0;
+								channel->played_patjump_order = 0xFFFE;
+							}
+							*/
+							else
+							{
+								bit_array_destroy(channel->played_patjump);
+								channel->played_patjump = 0;
+								channel->played_patjump_order = 0xFFFE;
+							}
+						}
+					}
+				}
+			}
+#endif
+
+			sigrenderer->processrow++;
+
+			if (sigrenderer->processrow >= sigrenderer->n_rows) {
+				IT_PATTERN *pattern;
+				int n;
+				int processorder = sigrenderer->processorder;
+
+				sigrenderer->processrow = sigrenderer->breakrow;
+				sigrenderer->breakrow = 0;
+
+				if (sigrenderer->processorder == 0xFFFF)
+					sigrenderer->processorder = sigrenderer->order - 1;
+
+				for (;;) {
+					sigrenderer->processorder++;
+
+					if (sigrenderer->processorder >= sigdata->n_orders) {
+						sigrenderer->processorder = sigrenderer->restart_position;
+						if (sigrenderer->processorder >= sigdata->n_orders) {
+							/* Restarting beyond end. We'll loop for now. */
+							sigrenderer->processorder = -1;
+							continue;
+						}
+					}
+
+					n = sigdata->order[sigrenderer->processorder];
+
+					if (n < sigdata->n_patterns)
+						break;
+
+#ifdef INVALID_ORDERS_END_SONG
+					if (n != IT_ORDER_SKIP)
+#else
+					if (n == IT_ORDER_END)
+#endif
+					{
+						sigrenderer->processorder = sigrenderer->restart_position - 1;
+					}
+				}
+
+				pattern = &sigdata->pattern[n];
+
+				n = sigrenderer->n_rows;
+				sigrenderer->n_rows = pattern->n_rows;
+
+				if (sigrenderer->processrow >= sigrenderer->n_rows)
+					sigrenderer->processrow = 0;
+
+/** WARNING - everything pertaining to a new pattern initialised? */
+
+				sigrenderer->entry = sigrenderer->entry_start = pattern->entry;
+				sigrenderer->entry_end = sigrenderer->entry + pattern->n_entries;
+
+				/* If n_rows was 0, we're only just starting. Don't do anything weird here. */
+				/* added: process row check, for break to row spooniness */
+				if (n && (processorder == 0xFFFF ? sigrenderer->order > sigrenderer->processorder : sigrenderer->order >= sigrenderer->processorder)
+#ifdef BIT_ARRAY_BULLSHIT
+					&& bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow)
+#endif
+					) {
+					if (sigrenderer->callbacks->loop) {
+						if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data))
+							return 1;
+#ifdef BIT_ARRAY_BULLSHIT
+						bit_array_reset(sigrenderer->played);
+#endif
+						if (sigrenderer->speed == 0)
+							goto speed0; /* I love goto */
+					}
+				}
+				sigrenderer->order = sigrenderer->processorder;
+
+				n = sigrenderer->processrow;
+				while (n) {
+					while (sigrenderer->entry < sigrenderer->entry_end) {
+						if (IT_IS_END_ROW(sigrenderer->entry)) {
+							sigrenderer->entry++;
+							break;
+						}
+						sigrenderer->entry++;
+					}
+					n--;
+				}
+				sigrenderer->row = sigrenderer->processrow;
+			} else {
+				if (sigrenderer->entry) {
+					while (sigrenderer->entry < sigrenderer->entry_end) {
+						if (IT_IS_END_ROW(sigrenderer->entry)) {
+							sigrenderer->entry++;
+							break;
+						}
+						sigrenderer->entry++;
+					}
+					sigrenderer->row++;
+				} else {
+#ifdef BIT_ARRAY_BULLSHIT
+					bit_array_clear(sigrenderer->played, sigrenderer->order * 256);
+#endif
+					sigrenderer->entry = sigrenderer->entry_start;
+					sigrenderer->row = 0;
+				}
+			}
+
+			reset_effects(sigrenderer);
+
+			{
+				IT_ENTRY *entry = sigrenderer->entry;
+				int ignore_cxx = 0;
+
+				while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+					ignore_cxx |= update_pattern_variables(sigrenderer, entry++);
+
+				entry = sigrenderer->entry;
+
+				while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry))
+					if (process_entry(sigrenderer, entry++, sigdata->flags & IT_WAS_AN_XM ? 0 : ignore_cxx))
+						return 1;
+			}
+
+			if (!(sigdata->flags & IT_OLD_EFFECTS))
+				update_smooth_effects(sigrenderer);
+		} else {
+			{
+				IT_ENTRY *entry = sigrenderer->entry;
+
+				while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) {
+					if (entry->mask & IT_ENTRY_EFFECT && entry->effect != IT_SET_SAMPLE_OFFSET)
+						process_effects(sigrenderer, entry, 0);
+							/* Don't bother checking the return value; if there
+							 * was a pattern delay, there can't be a speed=0.
+							 */
+					entry++;
+				}
+			}
+
+			update_effects(sigrenderer);
+		}
+	} else {
+		speed0:
+		update_effects(sigrenderer);
+		update_tick_counts(sigrenderer);
+	}
+
+	if (sigrenderer->globalvolume == 0) {
+		if (sigrenderer->callbacks->global_volume_zero) {
+			LONG_LONG t = sigrenderer->gvz_sub_time + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo;
+			sigrenderer->gvz_time += (int)(t >> 16);
+			sigrenderer->gvz_sub_time = (int)t & 65535;
+			if (sigrenderer->gvz_time >= 65536 * 12) {
+				if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data))
+					return 1;
+			}
+		}
+	} else {
+		if (sigrenderer->callbacks->global_volume_zero) {
+			sigrenderer->gvz_time = 0;
+			sigrenderer->gvz_sub_time = 0;
+		}
+	}
+
+	process_all_playing(sigrenderer);
+
+	{
+		LONG_LONG t = sigrenderer->sub_time_left + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo;
+		sigrenderer->time_left += (int)(t >> 16);
+		sigrenderer->sub_time_left = (int)t & 65535;
+	}
+
+	return 0;
+}
+
+
+
+int dumb_it_max_to_mix = 64;
+
+static const int aiMODVol[] =
+{
+	0,
+		16, 24, 32, 48, 64, 80, 96, 112,
+		128, 144, 160, 176, 192, 208, 224, 240,
+		256, 272, 288, 304, 320, 336, 352, 368,
+		384, 400, 416, 432, 448, 464, 480, 496,
+		529, 545, 561, 577, 593, 609, 625, 641,
+		657, 673, 689, 705, 721, 737, 753, 769,
+		785, 801, 817, 833, 849, 865, 881, 897,
+		913, 929, 945, 961, 977, 993, 1009, 1024
+};
+
+static const int aiPTMVolScaled[] =
+{
+	0,
+		31, 54, 73, 96, 111, 130, 153, 172,
+		191, 206, 222, 237, 252, 275, 298, 317,
+		336, 351, 370, 386, 401, 416, 428, 443,
+		454, 466, 477, 489, 512, 531, 553, 573,
+		592, 611, 626, 645, 660, 679, 695, 710,
+		725, 740, 756, 767, 782, 798, 809, 820,
+		836, 847, 859, 870, 881, 897, 908, 916,
+		927, 939, 950, 962, 969, 983, 1005, 1024
+};
+
+static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume)
+{
+	if (volume != 0) {
+		int vol;
+
+		if (playing->channel->flags & IT_CHANNEL_MUTED)
+			return 0;
+
+		if ((playing->channel->tremor_time & 192) == 128)
+			return 0;
+
+		switch (playing->tremolo_waveform)
+		{
+		default:
+			vol = it_sine[playing->tremolo_time];
+			break;
+		case 1:
+			vol = it_sawtooth[playing->tremolo_time];
+			break;
+		case 2:
+			vol = it_squarewave[playing->tremolo_time];
+			break;
+		case 3:
+			vol = (rand() % 129) - 64;
+			break;
+		case 4:
+			vol = it_xm_squarewave[playing->vibrato_time];
+			break;
+		case 5:
+			vol = it_xm_ramp[playing->vibrato_time];
+			break;
+		case 6:
+			vol = it_xm_ramp[255-playing->vibrato_time];
+			break;
+		}
+		vol *= playing->tremolo_depth;
+
+		vol = (playing->volume << 5) + vol;
+
+		if (vol <= 0)
+			return 0;
+
+		if (vol > 64 << 5)
+			vol = 64 << 5;
+
+		volume *= vol; /* 64 << 5 */
+		volume *= playing->sample->global_volume; /* 64 */
+		volume *= playing->channel_volume; /* 64 */
+		volume *= sigrenderer->globalvolume; /* 128 */
+		volume *= sigrenderer->sigdata->mixing_volume; /* 128 */
+		volume *= 1.0f / ((64 << 5) * 64.0f * 64.0f * 128.0f * 128.0f);
+
+		if (volume && playing->instrument) {
+			if (playing->enabled_envelopes & IT_ENV_VOLUME) {
+				volume *= envelope_get_y(&playing->env_instrument->volume_envelope, &playing->volume_envelope);
+				volume *= 1.0f / (64 << IT_ENVELOPE_SHIFT);
+			}
+			volume *= playing->instrument->global_volume; /* 128 */
+			volume *= playing->fadeoutcount; /* 1024 */
+			volume *= 1.0f / (128.0f * 1024.0f);
+		}
+	}
+
+	return volume;
+}
+
+
+
+static int apply_pan_envelope(IT_PLAYING *playing)
+{
+	if (playing->pan <= 64 << IT_ENVELOPE_SHIFT) {
+		int pan;
+		if (playing->panbrello_depth) {
+			switch (playing->panbrello_waveform) {
+			default:
+				pan = it_sine[playing->panbrello_time];
+				break;
+			case 1:
+				pan = it_sawtooth[playing->panbrello_time];
+				break;
+			case 2:
+				pan = it_squarewave[playing->panbrello_time];
+				break;
+			case 3:
+				pan = playing->panbrello_random;
+				break;
+			}
+			pan *= playing->panbrello_depth << 3;
+
+			pan += playing->pan;
+			if (pan < 0) pan = 0;
+			else if (pan > 64 << IT_ENVELOPE_SHIFT) pan = 64 << IT_ENVELOPE_SHIFT;
+		} else {
+			pan = playing->pan;
+		}
+
+		if (playing->env_instrument && (playing->enabled_envelopes & IT_ENV_PANNING)) {
+			int p = envelope_get_y(&playing->env_instrument->pan_envelope, &playing->pan_envelope);
+			if (pan > 32 << IT_ENVELOPE_SHIFT)
+				p *= (64 << IT_ENVELOPE_SHIFT) - pan;
+			else
+				p *= pan;
+			pan += p >> (5 + IT_ENVELOPE_SHIFT);
+		}
+		return pan;
+	}
+	return playing->pan;
+}
+
+
+
+/* Note: if a click remover is provided, and store_end_sample is set, then
+ * the end point will be computed twice. This situation should not arise.
+ */
+static long render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int cr_record_which)
+{
+	int bits;
+
+	long size_rendered;
+
+	if (playing->flags & IT_PLAYING_DEAD)
+		return 0;
+
+	if (*left_to_mix <= 0)
+		volume = 0;
+
+#ifndef END_RAMPING
+	{
+		int quality = sigrenderer->resampling_quality;
+		if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+			quality = playing->sample->max_resampling_quality;
+		playing->resampler[0].quality = quality;
+		playing->resampler[1].quality = quality;
+	}
+#endif
+
+	bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8;
+
+#define RESAMPLERV(rv, resampler, dst, rampvol, rampdelta, volume, mix_volume)						 \
+{																	 \
+	rv = dumb_resample_n(bits, resampler, dst, size, rampvol, rampdelta, volume, mix_volume, delta); \
+	if (store_end_sample)											 \
+		(dst)[rv] = RESAMPLE_VALUE(resampler, *(rampvol) * mix_volume);				 \
+}
+
+#define RESAMPLE(resampler, dst, rampvol, rampdelta, volume, mix_volume)   \
+{										   \
+	int i;								   \
+	RESAMPLERV(i, resampler, dst, rampvol, rampdelta, volume, mix_volume); \
+}
+
+#define RESAMPLE_VALUE(resampler, volume) \
+	dumb_resample_get_current_sample_n(bits, resampler, volume)
+
+	if (volume == 0) {
+		size_rendered = dumb_resample_n(bits, &playing->resampler[0], NULL, size, 0, 0, 0, 0, delta);
+		if (playing->sample->flags & IT_SAMPLE_STEREO)
+			dumb_resample_n(bits, &playing->resampler[1], NULL, size, 0, 0, 0, 0, delta);
+	} else {
+		if (sigrenderer->n_channels == 2) {
+			DUMB_RESAMPLER start = playing->resampler[0];
+			if ((cr_record_which & 1) && sigrenderer->click_remover && sigrenderer->click_remover[0])
+				dumb_record_click(sigrenderer->click_remover[0], pos, RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[0] * volume));
+			RESAMPLERV(size_rendered, &playing->resampler[0], samples[0] + pos, &playing->ramp_volume[0], playing->ramp_delta[0] * main_delta, playing->float_volume[0], volume);
+			if ((cr_record_which & 2) && sigrenderer->click_remover && sigrenderer->click_remover[0])
+				dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[0] * volume));
+			if (playing->sample->flags & IT_SAMPLE_STEREO) {
+				if ((cr_record_which & 1) && sigrenderer->click_remover && sigrenderer->click_remover[1])
+					dumb_record_click(sigrenderer->click_remover[1], pos, RESAMPLE_VALUE(&playing->resampler[1], playing->ramp_volume[1] * volume));
+				RESAMPLE(&playing->resampler[1], samples[1] + pos, &playing->ramp_volume[1], playing->ramp_delta[1] * main_delta, playing->float_volume[1], volume);
+				if ((cr_record_which & 2) && sigrenderer->click_remover && sigrenderer->click_remover[1])
+					dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[1], playing->ramp_volume[1] * volume));
+			} else {
+				playing->resampler[0] = start;
+				if ((cr_record_which & 1) && sigrenderer->click_remover && sigrenderer->click_remover[1])
+					dumb_record_click(sigrenderer->click_remover[1], pos, RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[1] * volume));
+				RESAMPLE(&playing->resampler[0], samples[1] + pos, &playing->ramp_volume[1], playing->ramp_delta[1] * main_delta, playing->float_volume[1], volume);
+				if ((cr_record_which & 2) && sigrenderer->click_remover && sigrenderer->click_remover[1])
+					dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[1] * volume));
+			}
+		} else {
+			if (playing->sample->flags & IT_SAMPLE_STEREO) {
+				if (cr_record_which && sigrenderer->click_remover && sigrenderer->click_remover[0]) {
+					sample_t startstep, endstep;
+					startstep = RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[0] * volume);
+					RESAMPLE(&playing->resampler[0], samples[0] + pos, &playing->ramp_volume[0], playing->ramp_delta[0] * main_delta, playing->float_volume[0], volume);
+					endstep = RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[0] * volume);
+					startstep += RESAMPLE_VALUE(&playing->resampler[1], playing->ramp_volume[1] * volume);
+					RESAMPLERV(size_rendered, &playing->resampler[1], samples[0] + pos, &playing->ramp_volume[1], playing->ramp_delta[1] * main_delta, playing->float_volume[1], volume);
+					endstep += RESAMPLE_VALUE(&playing->resampler[1], playing->ramp_volume[1] * volume);
+					if (cr_record_which & 1) dumb_record_click(sigrenderer->click_remover[0], pos, startstep);
+					if (cr_record_which & 2) dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -endstep);
+				} else {
+					RESAMPLE(&playing->resampler[0], samples[0] + pos, &playing->ramp_volume[0], playing->ramp_delta[0] * main_delta, playing->float_volume[0], volume);
+					RESAMPLERV(size_rendered, &playing->resampler[1], samples[0] + pos, &playing->ramp_volume[1], playing->ramp_delta[1] * main_delta, playing->float_volume[1], volume);
+				}
+			} else {
+				if ((cr_record_which & 1) && sigrenderer->click_remover && sigrenderer->click_remover[0])
+					dumb_record_click(sigrenderer->click_remover[0], pos, RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[0] * volume));
+				RESAMPLERV(size_rendered, &playing->resampler[0], samples[0] + pos, &playing->ramp_volume[0], playing->ramp_delta[0] * main_delta, playing->float_volume[0], volume);
+				if ((cr_record_which & 2) && sigrenderer->click_remover && sigrenderer->click_remover[0])
+					dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -RESAMPLE_VALUE(&playing->resampler[0], playing->ramp_volume[0] * volume));
+			}
+		}
+		(*left_to_mix)--;
+	}
+
+	if (playing->resampler[0].dir == 0)
+		playing->flags |= IT_PLAYING_DEAD;
+
+	return size_rendered;
+}
+
+#ifdef END_RAMPING
+static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style)
+{
+	long rv, trv;
+	int l_t_m_temp, cr_which;
+	if (!size) return 0;
+
+	rv = 0;
+	cr_which = 3;
+
+	{
+		int quality = sigrenderer->resampling_quality;
+		if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality)
+			quality = playing->sample->max_resampling_quality;
+		playing->resampler[0].quality = quality;
+		playing->resampler[1].quality = quality;
+	}
+
+	if (ramp_style)
+	{
+		if (playing->declick_stage == 0) {
+			/* ramp up! */
+			cr_which = 1;
+			while (playing->declick_stage == 0 && size) {
+				l_t_m_temp = *left_to_mix;
+				if (size == 1) cr_which |= 2;
+				trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which);
+				cr_which &= 2;
+				if (ramp_style == 2) playing->declick_volume += (1.f / 256.f) * main_delta;
+				else playing->declick_volume *= (float)pow(2.0f, main_delta);
+				if (playing->declick_volume >= 1.0f) {
+					playing->declick_volume = 1.0f;
+					playing->declick_stage = 1;
+				}
+				if (!trv) {
+					*left_to_mix = l_t_m_temp;
+					return rv;
+				}
+				rv += trv;
+				pos += trv;
+				size -= trv;
+			}
+
+			if (!size) {
+				*left_to_mix = l_t_m_temp;
+				return rv;
+			}
+
+			cr_which = 2;
+
+		}
+#ifdef RAMP_DOWN
+		else if (playing->declick_stage == 2) {
+			float halflife = pow(0.5, 1.0 / 64.0 * main_delta);
+			cr_which = 1;
+			while (playing->declick_stage == 2 && size) {
+				l_t_m_temp = *left_to_mix;
+				if (size == 1) cr_which |= 2;
+				trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which);
+				cr_which &= 2;
+				/*if (ramp_style == 2) playing->declick_volume -= (1.f / 256.f) * main_delta;
+				else playing->declick_volume *= (float)pow(0.5f, main_delta);*/
+				playing->declick_volume *= halflife;
+				if (playing->declick_volume < (1.f / 256.f)) {
+					playing->declick_stage = 3;
+				}
+				if (!trv) {
+					*left_to_mix = l_t_m_temp;
+					return rv;
+				}
+				rv += trv;
+				pos += trv;
+				size -= trv;
+			}
+
+			if (size) rv += render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, left_to_mix, 2);
+			else *left_to_mix = l_t_m_temp;
+
+			return rv;
+
+		} else if (playing->declick_stage == 3) {
+			return size;
+		}
+#endif
+	} else if (playing->declick_stage >= 2) {
+		playing->declick_stage = 3;
+		return size;
+	}
+
+	return rv + render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, cr_which);
+}
+#else
+#define render_playing_ramp(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, ramp_style) render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, 3)
+#endif
+
+typedef struct IT_TO_MIX
+{
+	IT_PLAYING *playing;
+	float volume;
+}
+IT_TO_MIX;
+
+
+
+static int it_to_mix_compare(const void *e1, const void *e2)
+{
+	if (((const IT_TO_MIX *)e1)->volume > ((const IT_TO_MIX *)e2)->volume)
+		return -1;
+
+	if (((const IT_TO_MIX *)e1)->volume < ((const IT_TO_MIX *)e2)->volume)
+		return 1;
+
+	return 0;
+}
+
+
+
+static void apply_pitch_modifications(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing, float *delta, int *cutoff)
+{
+	{
+		int sample_vibrato_shift;
+		switch (playing->sample_vibrato_waveform)
+		{
+		default:
+			sample_vibrato_shift = it_sine[playing->sample_vibrato_time];
+			break;
+		case 1:
+			sample_vibrato_shift = it_sawtooth[playing->sample_vibrato_time];
+			break;
+		case 2:
+			sample_vibrato_shift = it_squarewave[playing->sample_vibrato_time];
+			break;
+		case 3:
+			sample_vibrato_shift = (rand() % 129) - 64;
+			break;
+		case 4:
+			sample_vibrato_shift = it_xm_squarewave[playing->sample_vibrato_time];
+			break;
+		case 5:
+			sample_vibrato_shift = it_xm_ramp[playing->sample_vibrato_time];
+			break;
+		case 6:
+			sample_vibrato_shift = it_xm_ramp[255-playing->sample_vibrato_time];
+			break;
+		}
+
+		if (sigdata->flags & IT_WAS_AN_XM) {
+			int depth = playing->sample->vibrato_depth; /* True depth */
+			if (playing->sample->vibrato_rate) {
+				depth *= playing->sample_vibrato_depth; /* Tick number */
+				depth /= playing->sample->vibrato_rate; /* XM sweep */
+			}
+			sample_vibrato_shift *= depth;
+		} else
+			sample_vibrato_shift *= playing->sample_vibrato_depth >> 8;
+
+		sample_vibrato_shift >>= 4;
+
+		if (sample_vibrato_shift) {
+			if ((sigdata->flags & IT_LINEAR_SLIDES) || !(sigdata->flags & IT_WAS_AN_XM))
+				*delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift);
+			else {
+				/* complicated! */
+				float scale = *delta / playing->delta;
+
+				*delta = (1.0f / 65536.0f) / playing->delta;
+
+				*delta -= sample_vibrato_shift / AMIGA_DIVISOR;
+
+				if (*delta < (1.0f / 65536.0f) / 32768.0f) {
+					*delta = (1.0f / 65536.0f) / 32768.0f;
+				}
+
+				*delta = (1.0f / 65536.0f) / *delta * scale;
+			}
+		}
+	}
+
+	if (playing->env_instrument &&
+		(playing->enabled_envelopes & IT_ENV_PITCH))
+	{
+		int p = envelope_get_y(&playing->env_instrument->pitch_envelope, &playing->pitch_envelope);
+		if (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_PITCH_IS_FILTER)
+			*cutoff = (*cutoff * (p+(32<<IT_ENVELOPE_SHIFT))) >> (6 + IT_ENVELOPE_SHIFT);
+		else
+			*delta *= (float)pow(DUMB_PITCH_BASE, p >> (IT_ENVELOPE_SHIFT - 7));
+	}
+}
+
+
+
+static void render(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long size, sample_t **samples)
+{
+	int i;
+
+	int n_to_mix = 0;
+	IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS];
+	int left_to_mix = dumb_it_max_to_mix;
+
+	sample_t **samples_to_filter = NULL;
+
+	int ramp_style = sigrenderer->ramp_style;
+
+	if (ramp_style > 2) {
+		if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == IT_WAS_AN_XM) ramp_style = 2;
+		else ramp_style -= 3;
+	}
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) {
+			to_mix[n_to_mix].playing = sigrenderer->channel[i].playing;
+			to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->channel[i].playing, volume);
+			n_to_mix++;
+		}
+	}
+
+	for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+		if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */
+			to_mix[n_to_mix].playing = sigrenderer->playing[i];
+			to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->playing[i], volume);
+			n_to_mix++;
+		}
+	}
+
+	if (volume != 0)
+		qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare);
+
+	for (i = 0; i < n_to_mix; i++) {
+		IT_PLAYING *playing = to_mix[i].playing;
+		float note_delta = delta * playing->delta;
+		int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT;
+
+		apply_pitch_modifications(sigrenderer->sigdata, playing, &note_delta, &cutoff);
+
+		if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) {
+			playing->true_filter_cutoff = cutoff;
+			playing->true_filter_resonance = playing->filter_resonance;
+		}
+
+		if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) {
+			if (!samples_to_filter) {
+				samples_to_filter = create_sample_buffer(sigrenderer->n_channels, size + 1);
+				if (!samples_to_filter) {
+					render_playing_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style);
+					continue;
+				}
+			}
+			{
+				long size_rendered;
+				DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover;
+				dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1));
+				sigrenderer->click_remover = NULL;
+				size_rendered = render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style);
+				sigrenderer->click_remover = cr;
+				it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0], pos, samples_to_filter[0], size_rendered,
+					65536.0f/delta, playing->true_filter_cutoff, playing->true_filter_resonance);
+				if (sigrenderer->n_channels == 2)
+					it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[1], pos, samples_to_filter[1], size_rendered,
+						65536.0f/delta, playing->true_filter_cutoff, playing->true_filter_resonance);
+				// warning: filtering is not prevented by low left_to_mix!
+			}
+		} else {
+			it_reset_filter_state(&playing->filter_state[0]);
+			it_reset_filter_state(&playing->filter_state[1]);
+			render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, samples, 0, &left_to_mix, ramp_style);
+		}
+	}
+
+	destroy_sample_buffer(samples_to_filter);
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		if (sigrenderer->channel[i].playing) {
+			//if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) {
+			// This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it.
+			if (
+#ifdef RAMP_DOWN
+			(sigrenderer->channel[i].playing->declick_stage == 3) || 
+#endif
+			(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) {
+				free(sigrenderer->channel[i].playing);
+				sigrenderer->channel[i].playing = NULL;
+			}
+		}
+	}
+
+	for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+		if (sigrenderer->playing[i]) {
+			if (
+#ifdef RAMP_DOWN
+				(sigrenderer->playing[i]->declick_stage == 3) ||
+#endif
+				(sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) {
+				free(sigrenderer->playing[i]);
+				sigrenderer->playing[i] = NULL;
+			}
+		}
+	}
+}
+
+
+
+static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder, IT_CALLBACKS *callbacks, DUMB_CLICK_REMOVER **cr)
+{
+	DUMB_IT_SIGRENDERER *sigrenderer;
+	int i;
+
+	if (startorder > sigdata->n_orders) {
+		free(callbacks);
+		dumb_destroy_click_remover_array(n_channels, cr);
+		return NULL;
+	}
+
+	sigrenderer = malloc(sizeof(*sigrenderer));
+	if (!sigrenderer) {
+		free(callbacks);
+		dumb_destroy_click_remover_array(n_channels, cr);
+		return NULL;
+	}
+
+	sigrenderer->callbacks = callbacks;
+	sigrenderer->click_remover = cr;
+
+	sigrenderer->sigdata = sigdata;
+	sigrenderer->n_channels = n_channels;
+	sigrenderer->resampling_quality = dumb_resampling_quality;
+	sigrenderer->ramp_style = 0;
+	sigrenderer->globalvolume = sigdata->global_volume;
+	sigrenderer->tempo = sigdata->tempo;
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+		IT_CHANNEL *channel = &sigrenderer->channel[i];
+#if IT_CHANNEL_MUTED != 1
+#error this is wrong
+#endif
+		channel->flags = sigdata->channel_pan[i] >> 7;
+		channel->volume = (sigdata->flags & IT_WAS_AN_XM) ? 0 : 64;
+		channel->pan = sigdata->channel_pan[i] & 0x7F;
+		channel->truepan = channel->pan << IT_ENVELOPE_SHIFT;
+		channel->channelvolume = sigdata->channel_volume[i];
+		channel->instrument = 0;
+		channel->sample = 0;
+		channel->note = 0;
+		channel->SFmacro = 0;
+		channel->filter_cutoff = 127;
+		channel->filter_resonance = 0;
+		channel->new_note_action = 0xFF;
+		channel->xm_retrig = 0;
+		channel->retrig_tick = 0;
+		channel->tremor_time = 0;
+		channel->vibrato_waveform = 0;
+		channel->tremolo_waveform = 0;
+		channel->panbrello_waveform = 0;
+		channel->glissando = 0;
+		channel->toneslide = 0;
+		channel->ptm_toneslide = 0;
+		channel->ptm_last_toneslide = 0;
+		channel->midi_state = 0;
+		channel->lastvolslide = 0;
+		channel->lastDKL = 0;
+		channel->lastEF = 0;
+		channel->lastG = 0;
+		channel->lastHspeed = 0;
+		channel->lastHdepth = 0;
+		channel->lastRspeed = 0;
+		channel->lastRdepth = 0;
+		channel->lastYspeed = 0;
+		channel->lastYdepth = 0;
+		channel->lastI = 0;
+		channel->lastJ = 0;
+		channel->lastN = 0;
+		channel->lastO = 0;
+		channel->high_offset = 0;
+		channel->lastP = 0;
+		channel->lastQ = 0;
+		channel->lastS = 0;
+		channel->pat_loop_row = 0;
+		channel->pat_loop_count = 0;
+		channel->lastW = 0;
+		channel->xm_lastE1 = 0;
+		channel->xm_lastE2 = 0;
+		channel->xm_lastEA = 0;
+		channel->xm_lastEB = 0;
+		channel->xm_lastX1 = 0;
+		channel->xm_lastX2 = 0;
+		channel->playing = NULL;
+#ifdef BIT_ARRAY_BULLSHIT
+		channel->played_patjump = NULL;
+		channel->played_patjump_order = 0xFFFE;
+#endif
+	}
+
+	for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+		sigrenderer->playing[i] = NULL;
+
+	sigrenderer->speed = sigdata->speed;
+
+	sigrenderer->processrow = 0xFFFE;
+	sigrenderer->n_rows = 0;
+	sigrenderer->breakrow = 0;
+	sigrenderer->rowcount = 1;
+	sigrenderer->order = startorder;
+	/* meh!
+	if (startorder > 0) {
+		int n;
+		for (n = startorder - 1; n >= 0; n--) {
+			if (sigdata->order[n] > sigdata->n_patterns) {
+				sigrenderer->restart_position = n + 1;
+				break;
+			}
+		}
+	}
+	*/
+	if (startorder > 0) {
+		sigrenderer->restart_position = startorder;
+	} else {
+		sigrenderer->restart_position = sigdata->restart_position;
+	}
+
+	sigrenderer->row = 0;
+	sigrenderer->processorder = startorder - 1;
+	sigrenderer->tick = 1;
+
+	{
+		int order;
+		for (order = 0; order < sigdata->n_orders; order++) {
+			int n = sigdata->order[order];
+			if (n < sigdata->n_patterns) goto found_valid_order;
+#ifdef INVALID_ORDERS_END_SONG
+			if (n != IT_ORDER_SKIP)
+#else
+			if (n == IT_ORDER_END)
+#endif
+				break;
+		}
+		/* If we get here, there were no valid orders in the song. */
+		_dumb_it_end_sigrenderer(sigrenderer);
+		return NULL;
+	}
+	found_valid_order:
+
+	sigrenderer->time_left = 0;
+	sigrenderer->sub_time_left = 0;
+
+#ifdef BIT_ARRAY_BULLSHIT
+	sigrenderer->played = bit_array_create(sigdata->n_orders * 256);
+#endif
+
+	sigrenderer->gvz_time = 0;
+	sigrenderer->gvz_sub_time = 0;
+
+	return sigrenderer;
+}
+
+
+void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality)
+{
+	if (sigrenderer && quality >= 0 && quality < DUMB_RQ_N_LEVELS)
+	{
+		int i;
+		sigrenderer->resampling_quality = quality;
+		for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+			if (sigrenderer->channel[i].playing)
+			{
+				IT_PLAYING * playing = sigrenderer->channel[i].playing;
+				playing->resampling_quality = quality;
+				playing->resampler[0].quality = quality;
+				playing->resampler[1].quality = quality;
+			}
+		}
+		for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+			if (sigrenderer->playing[i]) {
+				IT_PLAYING * playing = sigrenderer->playing[i];
+				playing->resampling_quality = quality;
+				playing->resampler[0].quality = quality;
+				playing->resampler[1].quality = quality;
+			}
+		}
+	}
+}
+
+
+void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style) {
+	if (sigrenderer && ramp_style >= 0 && ramp_style <= 4) {
+		sigrenderer->ramp_style = ramp_style;
+	}
+}
+
+
+void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data)
+{
+	if (sigrenderer) {
+		sigrenderer->callbacks->loop = callback;
+		sigrenderer->callbacks->loop_data = data;
+	}
+}
+
+
+
+void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data)
+{
+	if (sigrenderer) {
+		sigrenderer->callbacks->xm_speed_zero = callback;
+		sigrenderer->callbacks->xm_speed_zero_data = data;
+	}
+}
+
+
+
+void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data)
+{
+	if (sigrenderer) {
+		sigrenderer->callbacks->midi = callback;
+		sigrenderer->callbacks->midi_data = data;
+	}
+}
+
+
+
+void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data)
+{
+	if (sigrenderer) {
+		sigrenderer->callbacks->global_volume_zero = callback;
+		sigrenderer->callbacks->global_volume_zero_data = data;
+	}
+}
+
+
+
+static IT_CALLBACKS *create_callbacks(void)
+{
+	IT_CALLBACKS *callbacks = malloc(sizeof(*callbacks));
+	if (!callbacks) return NULL;
+	callbacks->loop = NULL;
+	callbacks->xm_speed_zero = NULL;
+	callbacks->midi = NULL;
+	callbacks->global_volume_zero = NULL;
+	return callbacks;
+}
+
+
+
+static DUMB_IT_SIGRENDERER *dumb_it_init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder)
+{
+	IT_CALLBACKS *callbacks;
+
+	if (!sigdata) return NULL;
+
+	callbacks = create_callbacks();
+	if (!callbacks) return NULL;
+
+	return init_sigrenderer(sigdata, n_channels, startorder, callbacks,
+		dumb_create_click_remover_array(n_channels));
+}
+
+
+
+DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder)
+{
+	DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh);
+	DUMB_IT_SIGRENDERER *itsr = dumb_it_init_sigrenderer(itsd, n_channels, startorder);
+	/*duh->length = _dumb_it_build_checkpoints(itsd, startorder);*/
+	return duh_encapsulate_it_sigrenderer(itsr, n_channels, 0);
+}
+
+
+
+static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_channels, long pos)
+{
+	DUMB_IT_SIGDATA *sigdata = vsigdata;
+	DUMB_IT_SIGRENDERER *sigrenderer;
+
+	(void)duh;
+
+	{
+		IT_CALLBACKS *callbacks = create_callbacks();
+		if (!callbacks) return NULL;
+
+		if (sigdata->checkpoint) {
+			IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
+			while (checkpoint->next && checkpoint->next->time < pos)
+				checkpoint = checkpoint->next;
+			sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, n_channels, callbacks);
+			if (!sigrenderer) return NULL;
+			sigrenderer->click_remover = dumb_create_click_remover_array(n_channels);
+			pos -= checkpoint->time;
+		} else {
+			sigrenderer = init_sigrenderer(sigdata, n_channels, 0, callbacks,
+				dumb_create_click_remover_array(n_channels));
+			if (!sigrenderer) return NULL;
+		}
+	}
+
+	while (pos >= sigrenderer->time_left) {
+		render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL);
+
+		pos -= sigrenderer->time_left;
+		sigrenderer->time_left = 0;
+
+		if (process_tick(sigrenderer)) {
+			_dumb_it_end_sigrenderer(sigrenderer);
+			return NULL;
+		}
+	}
+
+	render(sigrenderer, 0, 1.0f, 0, pos, NULL);
+	sigrenderer->time_left -= pos;
+
+	return sigrenderer;
+}
+
+
+
+static long it_sigrenderer_get_samples(
+	sigrenderer_t *vsigrenderer,
+	float volume, float delta,
+	long size, sample_t **samples
+)
+{
+	DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+	long pos;
+	int dt;
+	long todo;
+	LONG_LONG t;
+
+	if (sigrenderer->order < 0) return 0; // problematic
+
+	pos = 0;
+	dt = (int)(delta * 65536.0f + 0.5f);
+
+	/* When samples is finally used in render_playing(), it won't be used if
+	 * volume is 0.
+	 */
+	if (!samples) volume = 0;
+
+	for (;;) {
+		todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt);
+
+		if (todo >= size)
+			break;
+
+		render(sigrenderer, volume, delta, pos, todo, samples);
+
+		pos += todo;
+		size -= todo;
+
+		t = sigrenderer->sub_time_left - (LONG_LONG)todo * dt;
+		sigrenderer->sub_time_left = (long)t & 65535;
+		sigrenderer->time_left += (long)(t >> 16);
+
+		if (process_tick(sigrenderer)) {
+			sigrenderer->order = -1;
+			sigrenderer->row = -1;
+			return pos;
+		}
+	}
+
+	render(sigrenderer, volume, delta, pos, size, samples);
+
+	pos += size;
+
+	t = sigrenderer->sub_time_left - (LONG_LONG)size * dt;
+	sigrenderer->sub_time_left = (long)t & 65535;
+	sigrenderer->time_left += (long)(t >> 16);
+
+	if (volume) dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta);
+
+	return pos;
+}
+
+
+
+static void it_sigrenderer_get_current_sample(sigrenderer_t *vsigrenderer, float volume, sample_t *samples)
+{
+	DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+	(void)volume; // for consideration: in any of these such functions, is 'volume' going to be required?
+	dumb_click_remover_get_offset_array(sigrenderer->n_channels, sigrenderer->click_remover, samples);
+}
+
+
+
+void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer)
+{
+	DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer;
+
+	int i;
+
+	if (sigrenderer) {
+		for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
+			if (sigrenderer->channel[i].playing)
+				free(sigrenderer->channel[i].playing);
+#ifdef BIT_ARRAY_BULLSHIT
+			bit_array_destroy(sigrenderer->channel[i].played_patjump);
+#endif
+		}
+
+		for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
+			if (sigrenderer->playing[i])
+				free(sigrenderer->playing[i]);
+
+		dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover);
+
+		if (sigrenderer->callbacks)
+			free(sigrenderer->callbacks);
+
+#ifdef BIT_ARRAY_BULLSHIT
+		bit_array_destroy(sigrenderer->played);
+#endif
+
+		free(vsigrenderer);
+	}
+}
+
+
+
+DUH_SIGTYPE_DESC _dumb_sigtype_it = {
+	SIGTYPE_IT,
+	NULL,
+	&it_start_sigrenderer,
+	NULL,
+	&it_sigrenderer_get_samples,
+	&it_sigrenderer_get_current_sample,
+	&_dumb_it_end_sigrenderer,
+	&_dumb_it_unload_sigdata
+};
+
+
+
+DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos)
+{
+	return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos);
+}
+
+
+
+DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer)
+{
+	return duh_get_raw_sigrenderer(sigrenderer, SIGTYPE_IT);
+}
+
+
+
+/* Values of 64 or more will access NNA channels here. */
+void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state)
+{
+	IT_PLAYING *playing;
+	int t; /* temporary var for holding accurate pan and filter cutoff */
+	float delta;
+	ASSERT(channel < DUMB_IT_TOTAL_CHANNELS);
+	if (!sr) { state->sample = 0; return; }
+	if (channel >= DUMB_IT_N_CHANNELS) {
+		playing = sr->playing[channel - DUMB_IT_N_CHANNELS];
+		if (!playing) { state->sample = 0; return; }
+	} else {
+		playing = sr->channel[channel].playing;
+		if (!playing) { state->sample = 0; return; }
+	}
+
+	if (playing->flags & IT_PLAYING_DEAD) { state->sample = 0; return; }
+
+	state->channel = playing->channel - sr->channel;
+	state->sample = playing->sampnum;
+	state->volume = calculate_volume(sr, playing, 1.0f);
+
+	t = apply_pan_envelope(playing);
+	state->pan = (unsigned char)((t + 128) >> IT_ENVELOPE_SHIFT);
+	state->subpan = (signed char)t;
+
+	delta = playing->delta * 65536.0f;
+	t = playing->filter_cutoff << IT_ENVELOPE_SHIFT;
+	apply_pitch_modifications(sr->sigdata, playing, &delta, &t);
+	state->freq = (int)delta;
+	if (t == 127 << IT_ENVELOPE_SHIFT && playing->filter_resonance == 0) {
+		state->filter_resonance = playing->true_filter_resonance;
+		t = playing->true_filter_cutoff;
+	} else
+		state->filter_resonance = playing->filter_resonance;
+	state->filter_cutoff = (unsigned char)(t >> 8);
+	state->filter_subcutoff = (unsigned char)t;
+}
+
+
+
+int dumb_it_callback_terminate(void *data)
+{
+	(void)data;
+	return 1;
+}
+
+
+
+int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte)
+{
+	(void)data;
+	(void)channel;
+	(void)midi_byte;
+	return 1;
+}
+
+
+
+#define IT_CHECKPOINT_INTERVAL (30 * 65536) /* Half a minute */
+
+#define FUCKIT_THRESHOLD (120 * 60 * 65536) /* two hours? probably a pattern loop mess... */
+
+/* Returns the length of the module, up until it first loops. */
+long _dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder)
+{
+	IT_CHECKPOINT *checkpoint = malloc(sizeof(*checkpoint));
+	if (!checkpoint) return 0;
+	checkpoint->time = 0;
+	checkpoint->sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, startorder);
+	if (!checkpoint->sigrenderer) {
+		free(checkpoint);
+		return 0;
+	}
+	checkpoint->sigrenderer->callbacks->loop = &dumb_it_callback_terminate;
+	checkpoint->sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate;
+	checkpoint->sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate;
+
+	if (sigdata->checkpoint)
+	{
+		IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
+		while (checkpoint) {
+			IT_CHECKPOINT *next = checkpoint->next;
+			_dumb_it_end_sigrenderer(checkpoint->sigrenderer);
+			free(checkpoint);
+			checkpoint = next;
+		}
+	}
+
+	sigdata->checkpoint = checkpoint;
+
+	for (;;) {
+		long l;
+		DUMB_IT_SIGRENDERER *sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, 0, checkpoint->sigrenderer->callbacks);
+		checkpoint->sigrenderer->callbacks = NULL;
+		if (!sigrenderer) {
+			checkpoint->next = NULL;
+			return checkpoint->time;
+		}
+
+		l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL);
+		if (l < IT_CHECKPOINT_INTERVAL) {
+			_dumb_it_end_sigrenderer(sigrenderer);
+			checkpoint->next = NULL;
+			return checkpoint->time + l;
+		}
+
+		checkpoint->next = malloc(sizeof(*checkpoint->next));
+		if (!checkpoint->next) {
+			_dumb_it_end_sigrenderer(sigrenderer);
+			return checkpoint->time + IT_CHECKPOINT_INTERVAL;
+		}
+
+		checkpoint->next->time = checkpoint->time + IT_CHECKPOINT_INTERVAL;
+		checkpoint = checkpoint->next;
+		checkpoint->sigrenderer = sigrenderer;
+
+		if (checkpoint->time >= FUCKIT_THRESHOLD) {
+			checkpoint->next = NULL;
+			return 0;
+		}
+	}
+}
+
+static int is_pattern_silent(IT_PATTERN * pattern, int order) {
+	int ret = 1;
+	IT_ENTRY * entry, * end;
+	if (!pattern || !pattern->n_rows || !pattern->n_entries || !pattern->entry) return 1;
+
+	entry = pattern->entry;
+	end = entry + pattern->n_entries;
+
+	while (entry < end) {
+		if (!IT_IS_END_ROW(entry)) {
+			if (entry->mask & (IT_ENTRY_INSTRUMENT | IT_ENTRY_VOLPAN))
+				return 0;
+			if (entry->mask & IT_ENTRY_NOTE && entry->note < 120)
+				return 0;
+			if (entry->mask & IT_ENTRY_EFFECT) {
+				switch (entry->effect) {
+					case IT_SET_GLOBAL_VOLUME:
+						if (entry->effectvalue) return 0;
+						break;
+
+					case IT_SET_SPEED:
+						if (entry->effectvalue > 64) ret++;
+						break;
+
+					case IT_SET_SONG_TEMPO:
+					case IT_XM_KEY_OFF:
+						break;
+
+					case IT_JUMP_TO_ORDER:
+						if (entry->effectvalue != order)
+							return 0;
+						break;
+
+					case IT_S:
+						switch (entry->effectvalue >> 4) {
+							case IT_S_FINE_PATTERN_DELAY:
+							case IT_S_PATTERN_LOOP:
+							case IT_S_PATTERN_DELAY:
+								ret++;
+								break;
+
+							case IT_S7:
+								if ((entry->effectvalue & 15) > 2)
+									return 0;
+								break;
+
+							default:
+								return 0;
+						}
+						break;
+
+					default:
+						return 0;
+				}
+			}
+		}
+		entry++;
+	}
+
+	return ret;
+}
+
+int dumb_it_trim_silent_patterns(DUH * duh) {
+	int n;
+	DUMB_IT_SIGDATA *sigdata;
+
+	if (!duh) return -1;
+
+	sigdata = duh_get_it_sigdata(duh);
+
+	if (!sigdata || !sigdata->order || !sigdata->pattern) return -1;
+
+	for (n = 0; n < sigdata->n_orders; n++) {
+		int p = sigdata->order[n];
+		if (p < sigdata->n_patterns) {
+			IT_PATTERN * pattern = &sigdata->pattern[p];
+			if (is_pattern_silent(pattern, n) > 1) {
+				pattern->n_rows = 1;
+				pattern->n_entries = 0;
+				if (pattern->entry)
+				{
+					free(pattern->entry);
+					pattern->entry = NULL;
+				}
+			} else
+				break;
+		}
+	}
+
+	if (n == sigdata->n_orders) return -1;
+
+	for (n = sigdata->n_orders - 1; n >= 0; n--) {
+		int p = sigdata->order[n];
+		if (p < sigdata->n_patterns) {
+			IT_PATTERN * pattern = &sigdata->pattern[p];
+			if (is_pattern_silent(pattern, n) > 1) {
+				pattern->n_rows = 1;
+				pattern->n_entries = 0;
+				if (pattern->entry)
+				{
+					free(pattern->entry);
+					pattern->entry = NULL;
+				}
+			} else
+				break;
+		}
+	}
+
+	if (n < 0) return -1;
+
+	/*duh->length = _dumb_it_build_checkpoints(sigdata, 0);*/
+
+	return 0;
+}
+
+int dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data)
+{
+	int n;
+	long length;
+	void * ba_played;
+	DUMB_IT_SIGRENDERER * sigrenderer;
+	
+	if (!sigdata->n_orders || !sigdata->order) return -1;
+
+	ba_played = bit_array_create(sigdata->n_orders * 256);
+	if (!ba_played) return -1;
+
+	for (n = 0; n < sigdata->n_orders; n++) {
+		if ((sigdata->order[n] >= sigdata->n_patterns) ||
+			(is_pattern_silent(&sigdata->pattern[sigdata->order[n]], n)))
+			bit_array_set(ba_played, n * 256);
+	}
+
+	for (;;) {
+		for (n = 0; n < sigdata->n_orders; n++) {
+			if (!bit_array_test_range(ba_played, n * 256, 256)) break;
+		}
+
+		if (n == sigdata->n_orders) break;
+
+		sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, n);
+		if (!sigrenderer) {
+			bit_array_destroy(ba_played);
+			return -1;
+		}
+		sigrenderer->callbacks->loop = &dumb_it_callback_terminate;
+		sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate;
+		sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate;
+
+		length = 0;
+
+		for (;;) {
+			long l;
+
+			l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL);
+			length += l;
+			if (l < IT_CHECKPOINT_INTERVAL || length >= FUCKIT_THRESHOLD) {
+				/* SONG OVA! */
+				break;
+			}
+		}
+
+		if ((*callback)(callback_data, n, length) < 0) return -1;
+
+		bit_array_merge(ba_played, sigrenderer->played, 0);
+
+		_dumb_it_end_sigrenderer(sigrenderer);
+	}
+
+	bit_array_destroy(ba_played);
+
+	return 0;
+}
--- /dev/null
+++ b/dumb/src/it/itunload.c
@@ -1,0 +1,74 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * itunload.c - Code to free an Impulse Tracker       / / \  \
+ *              module from memory.                  | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+void _dumb_it_unload_sigdata(sigdata_t *vsigdata)
+{
+	if (vsigdata) {
+		DUMB_IT_SIGDATA *sigdata = vsigdata;
+		int n;
+
+		if (sigdata->song_message)
+			free(sigdata->song_message);
+
+		if (sigdata->order)
+			free(sigdata->order);
+
+		if (sigdata->instrument)
+			free(sigdata->instrument);
+
+		if (sigdata->sample) {
+			for (n = 0; n < sigdata->n_samples; n++) {
+				if (sigdata->sample[n].left)
+					free(sigdata->sample[n].left);
+				if (sigdata->sample[n].right)
+					free(sigdata->sample[n].right);
+			}
+			free(sigdata->sample);
+		}
+
+		if (sigdata->pattern) {
+			for (n = 0; n < sigdata->n_patterns; n++)
+				if (sigdata->pattern[n].entry)
+					free(sigdata->pattern[n].entry);
+			free(sigdata->pattern);
+		}
+
+		if (sigdata->midi)
+			free(sigdata->midi);
+
+		{
+			IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
+			while (checkpoint) {
+				IT_CHECKPOINT *next = checkpoint->next;
+				_dumb_it_end_sigrenderer(checkpoint->sigrenderer);
+				free(checkpoint);
+				checkpoint = next;
+			}
+		}
+
+		free(vsigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/load669.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loadmod.c - Code to read a 669 Composer module     / / \  \
+ *             file, opening and closing it for      | <  /   \_
+ *             you.                                  |  \/ /\   /
+ *                                                    \_  /  > /
+ * By Chris Moeller, mostly based on loadmod.c          | \ / /
+ * by entheh.                                           |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_669(): loads a 669 file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_669(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_669(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/loadmod.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loadmod.c - Code to read a good old-fashioned      / / \  \
+ *             Amiga module file, opening and        | <  /   \_
+ *             closing it for you.                   |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_mod(): loads a MOD file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_mod(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_mod(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/loadmtm.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loadmtm.c - Code to read a MultiTracker Module     / / \  \
+ *             file, opening and closing it for      | <  /   \_
+ *             you.                                  |  \/ /\   /
+ *                                                    \_  /  > /
+ * By Chris Moeller, mostly based on loadmod.c          | \ / /
+ * by entheh.                                           |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_mtm(): loads a MTM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_mtm(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_mtm(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/loadoldpsm.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loads3m.c - Code to read a ProTracker Studio       / / \  \
+ *             file, opening and closing it for      | <  /   \_
+ *             you.                                  |  \/ /\   /
+ *                                                    \_  /  > /
+ * By Chris Moeller.                                    | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_psm(): loads an old PSM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_old_psm(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_old_psm(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/loadpsm.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loads3m.c - Code to read a ProTracker Studio       / / \  \
+ *             file, opening and closing it for      | <  /   \_
+ *             you.                                  |  \/ /\   /
+ *                                                    \_  /  > /
+ * By Chris Moeller.                                    | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_psm(): loads a PSM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_psm(const char *filename, int subsong)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_psm(f, subsong);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/loads3m.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loads3m.c - Code to read a ScreamTracker 3         / / \  \
+ *             file, opening and closing it for      | <  /   \_
+ *             you.                                  |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_s3m(): loads an S3M file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_s3m(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_s3m(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/loadxm.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loadxm.c - Code to read a Fast Tracker II          / / \  \
+ *            file, opening and closing it for       | <  /   \_
+ *            you.                                   |  \/ /\   /
+ *                                                    \_  /  > /
+ * By entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_xm(): loads an XM file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_load_xm(const char *filename)
+{
+	DUH *duh;
+	DUMBFILE *f = dumbfile_open(filename);
+
+	if (!f)
+		return NULL;
+
+	duh = dumb_read_xm(f);
+
+	dumbfile_close(f);
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/ptmeffect.c
@@ -1,0 +1,125 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * ptmeffect.c - Code for converting PTM              / / \  \
+ *               effects to IT effects.              | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller. Based on xmeffect.c              \_  /  > /
+ * by Julien Cugniere.                                  | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry)
+{
+	if (effect >= PTM_N_EFFECTS)
+		return;
+
+	/* Linearisation of the effect number... */
+	if (effect == PTM_E) {
+		effect = PTM_EBASE + HIGH(value);
+		value = LOW(value);
+	}
+
+	/* convert effect */
+	entry->mask |= IT_ENTRY_EFFECT;
+	switch (effect) {
+
+		case PTM_APPREGIO:           effect = IT_ARPEGGIO;           break;
+		case PTM_PORTAMENTO_UP:      effect = IT_PORTAMENTO_UP;      break;
+		case PTM_PORTAMENTO_DOWN:    effect = IT_PORTAMENTO_DOWN;    break;
+		case PTM_TONE_PORTAMENTO:    effect = IT_TONE_PORTAMENTO;    break;
+		case PTM_VIBRATO:            effect = IT_VIBRATO;            break;
+		case PTM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break;
+		case PTM_VOLSLIDE_VIBRATO:   effect = IT_VOLSLIDE_VIBRATO;   break;
+		case PTM_TREMOLO:            effect = IT_TREMOLO;            break;
+		case PTM_SAMPLE_OFFSET:      effect = IT_SET_SAMPLE_OFFSET;  break;
+		case PTM_VOLUME_SLIDE:       effect = IT_VOLUME_SLIDE;       break;
+		case PTM_POSITION_JUMP:      effect = IT_JUMP_TO_ORDER;      break;
+		case PTM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break;
+		case PTM_PATTERN_BREAK:      effect = IT_BREAK_TO_ROW;       break;
+		case PTM_SET_GLOBAL_VOLUME:  effect = IT_SET_GLOBAL_VOLUME;  break;
+		case PTM_RETRIGGER:          effect = IT_RETRIGGER_NOTE;     break;
+		case PTM_FINE_VIBRATO:       effect = IT_FINE_VIBRATO;       break;
+
+		/* TODO properly */
+		case PTM_NOTE_SLIDE_UP:          effect = IT_PTM_NOTE_SLIDE_UP;          break;
+		case PTM_NOTE_SLIDE_DOWN:        effect = IT_PTM_NOTE_SLIDE_DOWN;        break;
+		case PTM_NOTE_SLIDE_UP_RETRIG:   effect = IT_PTM_NOTE_SLIDE_UP_RETRIG;   break;
+		case PTM_NOTE_SLIDE_DOWN_RETRIG: effect = IT_PTM_NOTE_SLIDE_DOWN_RETRIG; break;
+
+		case PTM_SET_TEMPO_BPM:
+			effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
+			break;
+
+		case PTM_EBASE+PTM_E_SET_FINETUNE:          effect = SBASE+IT_S_FINETUNE;              break; /** TODO */
+		case PTM_EBASE+PTM_E_SET_LOOP:              effect = SBASE+IT_S_PATTERN_LOOP;          break;
+		case PTM_EBASE+PTM_E_NOTE_CUT:              effect = SBASE+IT_S_DELAYED_NOTE_CUT;      break;
+		case PTM_EBASE+PTM_E_NOTE_DELAY:            effect = SBASE+IT_S_NOTE_DELAY;            break;
+		case PTM_EBASE+PTM_E_PATTERN_DELAY:         effect = SBASE+IT_S_PATTERN_DELAY;         break;
+		case PTM_EBASE+PTM_E_SET_PANNING:           effect = SBASE+IT_S_SET_PAN;               break;
+
+		case PTM_EBASE+PTM_E_FINE_VOLSLIDE_UP:
+			effect = IT_VOLUME_SLIDE;
+			value = EFFECT_VALUE(value, 0xF);
+			break;
+
+		case PTM_EBASE + PTM_E_FINE_VOLSLIDE_DOWN:
+			effect = IT_VOLUME_SLIDE;
+			value = EFFECT_VALUE(0xF, value);
+			break;
+
+		case PTM_EBASE + PTM_E_FINE_PORTA_UP:
+			effect = IT_PORTAMENTO_UP;
+			value = EFFECT_VALUE(0xF, value);
+			break;
+
+		case PTM_EBASE + PTM_E_FINE_PORTA_DOWN:
+			effect = IT_PORTAMENTO_DOWN;
+			value = EFFECT_VALUE(0xF, value);
+			break;
+
+		case PTM_EBASE + PTM_E_RETRIG_NOTE:
+			effect = IT_XM_RETRIGGER_NOTE;
+			value = EFFECT_VALUE(0, value);
+			break;
+
+		case PTM_EBASE + PTM_E_SET_VIBRATO_CONTROL:
+			effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM;
+			value &= ~4; /** TODO: value&4 -> don't retrig wave */
+			break;
+
+		case PTM_EBASE + PTM_E_SET_TREMOLO_CONTROL:
+			effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM;
+			value &= ~4; /** TODO: value&4 -> don't retrig wave */
+			break;
+
+		default:
+			/* user effect (often used in demos for synchronisation) */
+			entry->mask &= ~IT_ENTRY_EFFECT;
+	}
+
+	/* Inverse linearisation... */
+	if (effect >= SBASE && effect < SBASE+16) {
+		value = EFFECT_VALUE(effect-SBASE, value);
+		effect = IT_S;
+	}
+
+	entry->effect = effect;
+	entry->effectvalue = value;
+}
--- /dev/null
+++ b/dumb/src/it/read669.c
@@ -1,0 +1,453 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * read669.c - Code to read a 669 Composer module     / / \  \
+ *             from an open file.                    | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller.                                  \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+static int it_669_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int tempo, int breakpoint, unsigned char *buffer, int * used_channels)
+{
+	int pos;
+	int channel;
+	int row;
+	IT_ENTRY *entry;
+
+	pattern->n_rows = 64;
+
+	if (dumbfile_getnc(buffer, 64 * 3 * 8, f) < 64 * 3 * 8)
+		return -1;
+
+	/* compute number of entries */
+	pattern->n_entries = 64 + 1; /* Account for the row end markers, speed command */
+	if (breakpoint < 63) pattern->n_entries++; /* and break to row 0 */
+
+	pos = 0;
+	for (row = 0; row < 64; row++) {
+		for (channel = 0; channel < 8; channel++) {
+			if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF)
+				pattern->n_entries++;
+			pos += 3;
+		}
+	}
+
+	pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+	if (!pattern->entry)
+		return -1;
+
+	if (breakpoint == 63) breakpoint++;
+
+	entry = pattern->entry;
+
+	entry->channel = 0;
+	entry->mask = IT_ENTRY_EFFECT;
+	entry->effect = IT_SET_SPEED;
+	entry->effectvalue = tempo;
+	entry++;
+
+	pos = 0;
+	for (row = 0; row < 64; row++) {
+
+		if (row == breakpoint) {
+			entry->channel = 0;
+			entry->mask = IT_ENTRY_EFFECT;
+			entry->effect = IT_BREAK_TO_ROW;
+			entry->effectvalue = 0;
+			entry++;
+		}
+
+		for (channel = 0; channel < 8; channel++) {
+			if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF) {
+				entry->channel = channel;
+				entry->mask = 0;
+
+				if (buffer[pos+0] < 0xFE) {
+					entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT;
+					entry->note = (buffer[pos+0] >> 2) + 36;
+					entry->instrument = (((buffer[pos+0] << 4) | (buffer[pos+1] >> 4)) & 0x3F) + 1;
+				}
+				if (buffer[pos+0] <= 0xFE) {
+					entry->mask |= IT_ENTRY_VOLPAN;
+					entry->volpan = ((buffer[pos+1] & 15) << 6) / 15;
+					if (*used_channels < channel + 1) *used_channels = channel + 1;
+				}
+				if (buffer[pos+2] != 0xFF) {
+					entry->mask |= IT_ENTRY_EFFECT;
+					entry->effectvalue = buffer[pos+2] & 15;
+					switch (buffer[pos+2] >> 4) {
+						case 0:
+							entry->effect = IT_PORTAMENTO_UP;
+							break;
+						case 1:
+							entry->effect = IT_PORTAMENTO_DOWN;
+							break;
+						case 2:
+							entry->effect = IT_TONE_PORTAMENTO;
+							entry->effectvalue *= tempo; /* is this right? */
+							break;
+						case 3:
+							/*
+							entry->effect = IT_S;
+							entry->effectvalue |= IT_S_FINETUNE << 4;
+							*/
+							entry->effect = IT_PORTAMENTO_UP;
+							entry->effectvalue |= 0xE0;
+							break;
+						case 4:
+							entry->effect = IT_VIBRATO;
+							entry->effectvalue |= tempo << 4; /* is this right? */
+							break;
+						case 5:
+							if (entry->effectvalue) {
+								entry->effect = IT_SET_SPEED;
+								//entry->effectvalue += 2;
+							} else {
+								entry->mask &= ~IT_ENTRY_EFFECT;
+							}
+							break;
+						/* dunno about this, really... */
+						case 6:
+							if (entry->effectvalue == 0) {
+								entry->effect = IT_PANNING_SLIDE;
+								entry->effectvalue = 0xFE;
+							} else if (entry->effectvalue == 1) {
+								entry->effect = IT_PANNING_SLIDE;
+								entry->effectvalue = 0xEF;
+							} else {
+								entry->mask &= ~IT_ENTRY_EFFECT;
+							}
+							break;
+
+						default:
+							entry->mask &= ~IT_ENTRY_EFFECT;
+							break;
+					}
+					if (*used_channels < channel + 1) *used_channels = channel + 1;
+				}
+
+				entry++;
+			}
+			pos += 3;
+		}
+		IT_SET_END_ROW(entry);
+		entry++;
+	}
+
+	return 0;
+}
+
+
+
+static int it_669_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	dumbfile_getnc(sample->name, 13, f);
+	sample->name[13] = 0;
+
+	sample->filename[0] = 0;
+
+	sample->length = dumbfile_igetl(f);
+	sample->loop_start = dumbfile_igetl(f);
+	sample->loop_end = dumbfile_igetl(f);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	if (sample->length <= 0) {
+		sample->flags = 0;
+		return 0;
+	}
+
+	sample->flags = IT_SAMPLE_EXISTS;
+
+	sample->global_volume = 64;
+	sample->default_volume = 64;
+
+	sample->default_pan = 0;
+	sample->C5_speed = 8363;
+	// the above line might be wrong
+
+	if ((sample->loop_end > sample->length) && !(sample->loop_start))
+		sample->loop_end = 0;
+
+	if (sample->loop_end > sample->length)
+		sample->loop_end = sample->length;
+
+	if (sample->loop_end - sample->loop_start > 2)
+		sample->flags |= IT_SAMPLE_LOOP;
+
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = 0; // do we have to set _all_ these?
+	sample->max_resampling_quality = -1;
+
+	return 0;
+}
+
+
+
+static int it_669_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	long i;
+	long truncated_size;
+
+	/* let's get rid of the sample data coming after the end of the loop */
+	if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
+		truncated_size = sample->length - sample->loop_end;
+		sample->length = sample->loop_end;
+	} else {
+		truncated_size = 0;
+	}
+
+	sample->left = malloc(sample->length);
+
+	if (!sample->left)
+		return -1;
+
+	if (sample->length)
+	{
+		i = dumbfile_getnc(sample->left, sample->length, f);
+		
+		if (i < sample->length) {
+			//return -1;
+			// ficking truncated files
+			if (i <= 0) {
+				sample->flags = 0;
+				return 0;
+			}
+			sample->length = i;
+			if (sample->loop_end > i) sample->loop_end = i;
+		} else {
+			/* skip truncated data */
+			dumbfile_skip(f, truncated_size);
+			// Should we be truncating it?
+			if (dumbfile_error(f))
+				return -1;
+		}
+
+		for (i = 0; i < sample->length; i++)
+			((signed char *)sample->left)[i] ^= 0x80;
+	}
+
+	return 0;
+}
+
+
+static DUMB_IT_SIGDATA *it_669_load_sigdata(DUMBFILE *f, int * ext)
+{
+	DUMB_IT_SIGDATA *sigdata;
+	int n_channels;
+	int i;
+	unsigned char tempolist[128];
+	unsigned char breaklist[128];
+
+	i = dumbfile_igetw(f);
+	if (i != 0x6669 && i != 0x4E4A) return NULL;
+
+	*ext = (i == 0x4E4A);
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) {
+		return NULL;
+	}
+
+	if (dumbfile_getnc(sigdata->name, 36, f) < 36) {
+		free(sigdata);
+		return NULL;
+	}
+	sigdata->name[36] = 0;
+
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+	sigdata->sample = NULL;
+
+	sigdata->n_instruments = 0;
+
+	sigdata->song_message = malloc(72 + 2 + 1);
+	if (!sigdata->song_message) {
+		free(sigdata);
+		return NULL;
+	}
+	if (dumbfile_getnc(sigdata->song_message, 36, f) < 36) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	sigdata->song_message[36] = 13;
+	sigdata->song_message[36 + 1] = 10;
+	if (dumbfile_getnc(sigdata->song_message + 38, 36, f) < 36) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	sigdata->song_message[38 + 36] = 0;
+
+	sigdata->n_samples = dumbfile_getc(f);
+	sigdata->n_patterns = dumbfile_getc(f);
+	sigdata->restart_position = dumbfile_getc(f);
+
+	if ((sigdata->n_samples) > 64 || (sigdata->n_patterns > 128)) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	sigdata->order = malloc(128); /* We may need to scan the extra ones! */
+	if (!sigdata->order) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	if (dumbfile_getnc(sigdata->order, 128, f) < 128) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (i = 0; i < 128; i++) {
+		if (sigdata->order[i] == 255) break;
+		if (sigdata->order[i] >= sigdata->n_patterns) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+	}
+	if (!i) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	sigdata->n_orders = i;
+
+	if (dumbfile_getnc(tempolist, 128, f) < 128) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	if (dumbfile_getnc(breaklist, 128, f) < 128) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+	if (!sigdata->sample) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (i = 0; i < sigdata->n_samples; i++)
+		sigdata->sample[i].right = sigdata->sample[i].left = NULL;
+
+	for (i = 0; i < sigdata->n_samples; i++) {
+		if (it_669_read_sample_header(&sigdata->sample[i], f)) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+	}
+
+	/* May as well try to save a tiny bit of memory. */
+	if (sigdata->n_orders < 128) {
+		unsigned char *order = realloc(sigdata->order, sigdata->n_orders);
+		if (order) sigdata->order = order;
+	}
+
+	sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+	if (!sigdata->pattern) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	for (i = 0; i < sigdata->n_patterns; i++)
+		sigdata->pattern[i].entry = NULL;
+
+	n_channels = 0;
+
+	/* Read in the patterns */
+	{
+		unsigned char *buffer = malloc(64 * 3 * 8); /* 64 rows * 3 bytes * 8 channels */
+		if (!buffer) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (i = 0; i < sigdata->n_patterns; i++) {
+			if (it_669_read_pattern(&sigdata->pattern[i], f, tempolist[i], breaklist[i], buffer, &n_channels) != 0) {
+				free(buffer);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+		}
+		free(buffer);
+	}
+
+	sigdata->n_pchannels = n_channels;
+
+	/* And finally, the sample data */
+	for (i = 0; i < sigdata->n_samples; i++) {
+		if (it_669_read_sample_data(&sigdata->sample[i], f)) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+	}
+
+	/* Now let's initialise the remaining variables, and we're done! */
+	sigdata->flags = IT_OLD_EFFECTS | IT_LINEAR_SLIDES | IT_STEREO;
+
+	sigdata->global_volume = 128;
+	sigdata->mixing_volume = 48;
+	sigdata->speed = 4;
+	sigdata->tempo = 78;
+	sigdata->pan_separation = 128;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i += 2) {
+		sigdata->channel_pan[i+0] = 16;
+		sigdata->channel_pan[i+1] = 48;
+	}
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	return sigdata;
+}
+
+
+
+DUH *dumb_read_669(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+	int ext;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_669_load_sigdata(f, &ext);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = ext ? "669 Extended" : "669";
+		return make_duh(length, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readmod.c
@@ -1,0 +1,746 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readmod.c - Code to read a good old-fashioned      / / \  \
+ *             Amiga module from an open file.       | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Ben Davis.                                      \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+static int it_mod_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer)
+{
+	int pos;
+	int channel;
+	int row;
+	IT_ENTRY *entry;
+
+	pattern->n_rows = 64;
+
+	if (n_channels == 0) {
+		/* Read the first four channels, leaving gaps for the rest. */
+		for (pos = 0; pos < 64*8*4; pos += 8*4)
+			dumbfile_getnc(buffer + pos, 4*4, f);
+		/* Read the other channels into the gaps we left. */
+		for (pos = 4*4; pos < 64*8*4; pos += 8*4)
+			dumbfile_getnc(buffer + pos, 4*4, f);
+
+		n_channels = 8;
+	} else
+		dumbfile_getnc(buffer, 64 * n_channels * 4, f);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	/* compute number of entries */
+	pattern->n_entries = 64; /* Account for the row end markers */
+	pos = 0;
+	for (row = 0; row < 64; row++) {
+		for (channel = 0; channel < n_channels; channel++) {
+			if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3])
+				pattern->n_entries++;
+			pos += 4;
+		}
+	}
+
+	pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+	if (!pattern->entry)
+		return -1;
+
+	entry = pattern->entry;
+	pos = 0;
+	for (row = 0; row < 64; row++) {
+		for (channel = 0; channel < n_channels; channel++) {
+			if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) {
+				unsigned char sample = (buffer[pos+0] & 0xF0) | (buffer[pos+2] >> 4);
+				int period = ((int)(buffer[pos+0] & 0x0F) << 8) | buffer[pos+1];
+
+				entry->channel = channel;
+				entry->mask = 0;
+
+				if (period) {
+					int note;
+					entry->mask |= IT_ENTRY_NOTE;
+
+					/* frequency = (AMIGA_DIVISOR / 8) / (period * 2)
+					 * C-1: period = 214 -> frequency = 16726
+					 * so, set C5_speed to 16726
+					 * and period = 214 should translate to C5 aka 60
+					 * halve the period, go up an octive
+					 *
+					 * period = 214 / pow(DUMB_SEMITONE_BASE, note - 60)
+					 * pow(DUMB_SEMITONE_BASE, note - 60) = 214 / period
+					 * note - 60 = log(214/period) / log(DUMB_SEMITONE_BASE)
+					 */
+					note = (int)floor(log(214.0/period) / log(DUMB_SEMITONE_BASE) + 60.5);
+					entry->note = MID(0, note, 119);
+					// or should we preserve the period?
+					//entry->note = buffer[pos+0] & 0x0F; /* High nibble */
+					//entry->volpan = buffer[pos+1]; /* Low byte */
+					// and what about finetune?
+				}
+
+				if (sample) {
+					entry->mask |= IT_ENTRY_INSTRUMENT;
+					entry->instrument = sample;
+				}
+
+				_dumb_it_xm_convert_effect(buffer[pos+2] & 0x0F, buffer[pos+3], entry, 1);
+
+				entry++;
+			}
+			pos += 4;
+		}
+		IT_SET_END_ROW(entry);
+		entry++;
+	}
+
+	return 0;
+}
+
+
+
+static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	int finetune;
+
+/**
+     21       22   Chars     Sample 1 name.  If the name is not a full
+                             22 chars in length, it will be null
+                             terminated.
+
+If
+the sample name begins with a '#' character (ASCII $23 (35)) then this is
+assumed not to be an instrument name, and is probably a message.
+*/
+	dumbfile_getnc(sample->name, 22, f);
+	sample->name[22] = 0;
+
+	sample->filename[0] = 0;
+
+	sample->length = dumbfile_mgetw(f) << 1;
+	finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */
+/** Each  finetune step changes  the note 1/8th  of  a  semitone. */
+	sample->global_volume = 64;
+	sample->default_volume = dumbfile_getc(f); // Should we be setting global_volume to this instead?
+	sample->loop_start = dumbfile_mgetw(f) << 1;
+	sample->loop_end = sample->loop_start + (dumbfile_mgetw(f) << 1);
+/**
+Once this sample has been played completely from beginning
+to end, if the  repeat length (next field)  is greater than two  bytes it
+will loop back to this position in the sample and continue playing.  Once
+it has played for  the repeat length,  it continues to  loop back to  the
+repeat start offset.  This means the sample continues playing until it is
+told to stop.
+*/
+
+	if (sample->length <= 0) {
+		sample->flags = 0;
+		return 0;
+	}
+
+	sample->flags = IT_SAMPLE_EXISTS;
+
+	sample->default_pan = 0;
+	sample->C5_speed = (long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
+	// the above line might be wrong
+
+	if (sample->loop_end > sample->length)
+		sample->loop_end = sample->length;
+
+	if (sample->loop_end - sample->loop_start > 2)
+		sample->flags |= IT_SAMPLE_LOOP;
+
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = 0; // do we have to set _all_ these?
+	sample->max_resampling_quality = -1;
+
+	return dumbfile_error(f);
+}
+
+
+
+static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f, unsigned long fft)
+{
+	long i;
+	long truncated_size;
+
+	/* let's get rid of the sample data coming after the end of the loop */
+	if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
+		truncated_size = sample->length - sample->loop_end;
+		sample->length = sample->loop_end;
+	} else {
+		truncated_size = 0;
+	}
+
+	sample->left = malloc(sample->length);
+
+	if (!sample->left)
+		return -1;
+
+	/* Sample data are stored in "8-bit two's compliment format" (sic). */
+	/*
+	for (i = 0; i < sample->length; i++)
+		((signed char *)sample->left)[i] = dumbfile_getc(f);
+	*/
+	if (sample->length)
+	{
+		/* F U Olivier Lapicque */
+		if (sample->length >= 5)
+		{
+			i = dumbfile_getnc(sample->left, 5, f);
+			if (i == 5)
+			{
+				if (!memcmp(sample->left, "ADPCM", 5))
+				{
+					if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+						return -1;
+
+					return 0;
+				}
+				else
+				{
+					i += dumbfile_getnc(((char *)sample->left) + 5, sample->length - 5, f);
+				}
+			}
+		}
+		if (i < sample->length)
+		{
+			if (i <= 0)
+			{
+				sample->flags = 0;
+				return 0;
+			}
+			sample->length = i;
+			if (sample->loop_end > i) sample->loop_end = i;
+			// holy crap!
+			if (sample->loop_start > i) sample->flags &= ~IT_SAMPLE_LOOP;
+		}
+		else
+		{
+			/* skip truncated data */
+			int feh = dumbfile_error(f);
+
+			if (truncated_size) dumbfile_skip(f, truncated_size);
+			// Should we be truncating it?
+
+			if (feh)
+				return -1;
+		}
+
+		if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
+			int delta = 0;
+			for (i = 0; i < sample->length; i++) {
+				delta += ((signed char *)sample->left)[i];
+				((signed char *)sample->left)[i] = delta;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+
+typedef struct BUFFERED_MOD BUFFERED_MOD;
+
+struct BUFFERED_MOD
+{
+	unsigned char *buffered;
+	long ptr, len;
+	DUMBFILE *remaining;
+};
+
+
+
+static int buffer_mod_skip(void *f, long n)
+{
+	BUFFERED_MOD *bm = f;
+	if (bm->buffered) {
+		bm->ptr += n;
+		if (bm->ptr >= bm->len) {
+			free(bm->buffered);
+			bm->buffered = NULL;
+			return dumbfile_skip(bm->remaining, bm->ptr - bm->len);
+		}
+		return 0;
+	}
+	return dumbfile_skip(bm->remaining, n);
+}
+
+
+
+static int buffer_mod_getc(void *f)
+{
+	BUFFERED_MOD *bm = f;
+	if (bm->buffered) {
+		int rv = bm->buffered[bm->ptr++];
+		if (bm->ptr >= bm->len) {
+			free(bm->buffered);
+			bm->buffered = NULL;
+		}
+		return rv;
+	}
+	return dumbfile_getc(bm->remaining);
+}
+
+
+
+static long buffer_mod_getnc(char *ptr, long n, void *f)
+{
+	BUFFERED_MOD *bm = f;
+	if (bm->buffered) {
+		int left = bm->len - bm->ptr;
+		if (n >= left) {
+			memcpy(ptr, bm->buffered + bm->ptr, left);
+			free(bm->buffered);
+			bm->buffered = NULL;
+			if (n - left) {
+				int rv = dumbfile_getnc(ptr + left, n - left, bm->remaining);
+				return left + MAX(rv, 0);
+			} else {
+				return left;
+			}
+		}
+		memcpy(ptr, bm->buffered + bm->ptr, n);
+		bm->ptr += n;
+		return n;
+	}
+	return dumbfile_getnc(ptr, n, bm->remaining);
+}
+
+
+
+static void buffer_mod_close(void *f)
+{
+	BUFFERED_MOD *bm = f;
+	if (bm->buffered) free(bm->buffered);
+	/* Do NOT close bm->remaining */
+	free(f);
+}
+
+
+
+DUMBFILE_SYSTEM buffer_mod_dfs = {
+	NULL,
+	&buffer_mod_skip,
+	&buffer_mod_getc,
+	&buffer_mod_getnc,
+	&buffer_mod_close
+};
+
+
+
+#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128)
+
+static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, unsigned long *fft)
+{
+	BUFFERED_MOD *bm = malloc(sizeof(*bm));
+	if (!bm) return NULL;
+
+	bm->buffered = malloc(MOD_FFT_OFFSET + 4);
+	if (!bm->buffered) {
+		free(bm);
+		return NULL;
+	}
+
+	bm->len = dumbfile_getnc(bm->buffered, MOD_FFT_OFFSET + 4, f);
+
+	if (bm->len > 0) {
+		if (bm->len >= MOD_FFT_OFFSET + 4)
+			*fft = (unsigned long)bm->buffered[MOD_FFT_OFFSET  ] << 24
+			     | (unsigned long)bm->buffered[MOD_FFT_OFFSET+1] << 16
+			     | (unsigned long)bm->buffered[MOD_FFT_OFFSET+2] << 8
+			     | (unsigned long)bm->buffered[MOD_FFT_OFFSET+3];
+		else
+			*fft = 0;
+		bm->ptr = 0;
+	} else {
+		free(bm->buffered);
+		bm->buffered = NULL;
+	}
+
+	bm->remaining = f;
+
+	return dumbfile_open_ex(bm, &buffer_mod_dfs);
+}
+
+static DUMBFILE *dumbfile_buffer_mod_2(DUMBFILE *f, long *remain)
+{
+	long read;
+	BUFFERED_MOD *bm = malloc(sizeof(*bm));
+	if (!bm) return NULL;
+
+	bm->buffered = malloc(32768);
+	if (!bm->buffered) {
+		free(bm);
+		return NULL;
+	}
+
+	bm->len = 0;
+	*remain = 0;
+
+	read = dumbfile_getnc(bm->buffered, 32768, f);
+
+	if (read >= 0) {
+		bm->len += read;
+		*remain += read;
+
+		while (read >= 32768) {
+			bm->buffered = realloc(bm->buffered, *remain + 32768);
+			if (!bm->buffered) {
+				free(bm);
+				return 0;
+			}
+			read = dumbfile_getnc(bm->buffered + *remain, 32768, f);
+			if (read >= 0) {
+				bm->len += read;
+				*remain += read;
+			}
+		}
+	}
+
+	if (*remain) {
+		bm->ptr = 0;
+	} else {
+		free(bm->buffered);
+		bm->buffered = NULL;
+	}
+
+	bm->remaining = f;
+
+	return dumbfile_open_ex(bm, &buffer_mod_dfs);
+}
+
+
+static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f)
+{
+	DUMB_IT_SIGDATA *sigdata;
+	int n_channels;
+	int i;
+	unsigned long fft;
+	DUMBFILE *rem;
+
+	f = dumbfile_buffer_mod(f, &fft);
+	if (!f)
+		return NULL;
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) {
+		dumbfile_close(f);
+		return NULL;
+	}
+
+	/**
+      1       20   Chars     Title of the song.  If the title is not a
+                             full 20 chars in length, it will be null-
+                             terminated.
+	*/
+	if (dumbfile_getnc(sigdata->name, 20, f) < 20) {
+		free(sigdata);
+		dumbfile_close(f);
+		return NULL;
+	}
+	sigdata->name[20] = 0;
+
+	sigdata->n_samples = 31;
+
+	switch (fft) {
+		case DUMB_ID('M','.','K','.'):
+		case DUMB_ID('M','!','K','!'):
+		case DUMB_ID('M','&','K','!'):
+		case DUMB_ID('N','.','T','.'):
+		case DUMB_ID('N','S','M','S'):
+		case DUMB_ID('F','L','T','4'):
+		case DUMB_ID('M',0,0,0):
+		case DUMB_ID('8',0,0,0):
+			n_channels = 4;
+			break;
+		case DUMB_ID('F','L','T','8'):
+			n_channels = 0;
+			/* 0 indicates a special case; two four-channel patterns must be
+			 * combined into one eight-channel pattern. Pattern indexes must
+			 * be halved. Why oh why do they obfuscate so?
+			 */
+			for (i = 0; i < 128; i++)
+				sigdata->order[i] >>= 1;
+			break;
+		case DUMB_ID('C','D','8','1'):
+		case DUMB_ID('O','C','T','A'):
+		case DUMB_ID('O','K','T','A'):
+			n_channels = 8;
+			break;
+		case DUMB_ID('1','6','C','N'):
+			n_channels = 16;
+			break;
+		case DUMB_ID('3','2','C','N'):
+			n_channels = 32;
+			break;
+		default:
+			/* If we get an illegal tag, assume 4 channels 15 samples. */
+			if ((fft & 0x0000FFFFL) == DUMB_ID(0,0,'C','H')) {
+				if (fft >= '1' << 24 && fft < '4' << 24) {
+					n_channels = ((fft & 0x00FF0000L) >> 16) - '0';
+					if ((unsigned int)n_channels >= 10) {
+						/* Rightmost character wasn't a digit. */
+						n_channels = 4;
+						sigdata->n_samples = 15;
+					} else {
+						n_channels += (((fft & 0xFF000000L) >> 24) - '0') * 10;
+						/* MODs should really only go up to 32 channels, but we're lenient. */
+						if ((unsigned int)(n_channels - 1) >= DUMB_IT_N_CHANNELS - 1) {
+							/* No channels or too many? Can't be right... */
+							n_channels = 4;
+							sigdata->n_samples = 15;
+						}
+					}
+				} else {
+					n_channels = 4;
+					sigdata->n_samples = 15;
+				}
+			} else if ((fft & 0x00FFFFFFL) == DUMB_ID(0,'C','H','N')) {
+				n_channels = (fft >> 24) - '0';
+				if ((unsigned int)(n_channels - 1) >= 9) {
+					/* Character was '0' or it wasn't a digit */
+					n_channels = 4;
+					sigdata->n_samples = 15;
+				}
+			} else if ((fft & 0xFFFFFF00L) == DUMB_ID('T','D','Z',0)) {
+				n_channels = (fft & 0x000000FFL) - '0';
+				if ((unsigned int)(n_channels - 1) >= 9) {
+					/* We've been very lenient, given that it should have
+					 * been 1, 2 or 3, but this MOD has been very naughty and
+					 * must be punished.
+					 */
+					n_channels = 4;
+					sigdata->n_samples = 15;
+				}
+			} else {
+				n_channels = 4;
+				sigdata->n_samples = 15;
+			}
+	}
+
+	sigdata->n_pchannels = n_channels ? n_channels : 8; /* special case for 0, see above */
+
+	sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+	if (!sigdata->sample) {
+		free(sigdata);
+		dumbfile_close(f);
+		return NULL;
+	}
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_instruments = 0;
+
+	for (i = 0; i < sigdata->n_samples; i++)
+		sigdata->sample[i].right = sigdata->sample[i].left = NULL;
+
+	for (i = 0; i < sigdata->n_samples; i++) {
+		if (it_mod_read_sample_header(&sigdata->sample[i], f)) {
+			_dumb_it_unload_sigdata(sigdata);
+			dumbfile_close(f);
+			return NULL;
+		}
+	}
+
+	sigdata->n_orders = dumbfile_getc(f);
+	sigdata->restart_position = dumbfile_getc(f);
+	// what if this is >= 127? what about with Fast Tracker II?
+
+/*	if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right?
+		_dumb_it_unload_sigdata(sigdata);
+		dumbfile_close(f);
+		return NULL;
+	}*/
+
+	//if (sigdata->restart_position >= sigdata->n_orders)
+		//sigdata->restart_position = 0;
+
+	sigdata->order = malloc(128); /* We may need to scan the extra ones! */
+	if (!sigdata->order) {
+		_dumb_it_unload_sigdata(sigdata);
+		dumbfile_close(f);
+		return NULL;
+	}
+	if (dumbfile_getnc(sigdata->order, 128, f) < 128) {
+		_dumb_it_unload_sigdata(sigdata);
+		dumbfile_close(f);
+		return NULL;
+	}
+
+	if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right?
+		sigdata->n_orders = 128;
+		//while (sigdata->n_orders > 1 && !sigdata->order[sigdata->n_orders - 1]) sigdata->n_orders--;
+	}
+
+	/* "The old NST format contains only 15 samples (instead of 31). Further
+	 * it doesn't contain a file format tag (id). So Pattern data offset is
+	 * at 20+15*30+1+1+128."
+	 * - Then I shall assume the File Format Tag never exists if there are
+	 * only 15 samples. I hope this isn't a faulty assumption...
+	 */
+	if (sigdata->n_samples == 31)
+		dumbfile_skip(f, 4);
+
+	/* Work out how many patterns there are. */
+	sigdata->n_patterns = -1;
+	for (i = 0; i < 128; i++)
+		if (sigdata->n_patterns < sigdata->order[i])
+			sigdata->n_patterns = sigdata->order[i];
+	sigdata->n_patterns++;
+
+	/* May as well try to save a tiny bit of memory. */
+	if (sigdata->n_orders < 128) {
+		unsigned char *order = realloc(sigdata->order, sigdata->n_orders);
+		if (order) sigdata->order = order;
+	}
+
+	sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+	if (!sigdata->pattern) {
+		_dumb_it_unload_sigdata(sigdata);
+		dumbfile_close(f);
+		return NULL;
+	}
+	for (i = 0; i < sigdata->n_patterns; i++)
+		sigdata->pattern[i].entry = NULL;
+
+	/* Read in the patterns */
+	{
+		unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */
+		if (!buffer) {
+			_dumb_it_unload_sigdata(sigdata);
+			dumbfile_close(f);
+			return NULL;
+		}
+		for (i = 0; i < sigdata->n_patterns; i++) {
+			if (it_mod_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) {
+				free(buffer);
+				_dumb_it_unload_sigdata(sigdata);
+				dumbfile_close(f);
+				return NULL;
+			}
+		}
+		free(buffer);
+	}
+
+	rem = NULL;
+
+	/* uggly */
+	if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) {
+		long skip;
+		long remain;
+		rem = f;
+		f = dumbfile_buffer_mod_2(rem, &remain);
+		if (!f) {
+			_dumb_it_unload_sigdata(sigdata);
+			dumbfile_close(rem);
+			return NULL;
+		}
+		for (skip = 0, i = 0; i < sigdata->n_samples; i++) {
+			if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) {
+				skip += sigdata->sample[i].length;
+			}
+		}
+		if (remain - skip) {
+			if (dumbfile_skip(f, remain - skip)) {
+				_dumb_it_unload_sigdata(sigdata);
+				dumbfile_close(f);
+				dumbfile_close(rem);
+				return NULL;
+			}
+		}
+	}
+	
+
+	/* And finally, the sample data */
+	for (i = 0; i < sigdata->n_samples; i++) {
+		if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) {
+			_dumb_it_unload_sigdata(sigdata);
+			dumbfile_close(f);
+			if (rem) dumbfile_close(rem);
+			return NULL;
+		}
+	}
+
+	dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */
+	/* The DUMBFILE originally passed to our function is intact. */
+	if (rem) dumbfile_close(rem);
+
+	/* Now let's initialise the remaining variables, and we're done! */
+	sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
+
+	sigdata->global_volume = 128;
+	sigdata->mixing_volume = 48;
+	/* We want 50 ticks per second; 50/6 row advances per second;
+	 * 50*10=500 row advances per minute; 500/4=125 beats per minute.
+	 */
+	sigdata->speed = 6;
+	sigdata->tempo = 125;
+	sigdata->pan_separation = 128;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) {
+		sigdata->channel_pan[i+0] = 16;
+		sigdata->channel_pan[i+1] = 48;
+		sigdata->channel_pan[i+2] = 48;
+		sigdata->channel_pan[i+3] = 16;
+	}
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	return sigdata;
+}
+
+
+
+DUH *dumb_read_mod(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_mod_load_sigdata(f);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "MOD";
+		return make_duh(length, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readmtm.c
@@ -1,0 +1,413 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readmtm.c - Code to read a MultiTracker Module     / / \  \
+ *             from an open file.                    | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller.                                  \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+size_t strlen_max(const char * ptr, size_t max)
+{
+	const char * end, * start;
+	if (ptr==0) return 0;
+	start = ptr;
+	end = ptr + max;
+	while(*ptr && ptr < end) ptr++;
+	return ptr - start;
+}
+
+static int it_mtm_assemble_pattern(IT_PATTERN *pattern, const unsigned char * track, const unsigned short * sequence, int n_rows)
+{
+	int n, o, note, sample;
+	const unsigned char * t;
+	IT_ENTRY * entry;
+
+	pattern->n_rows = n_rows;
+	pattern->n_entries = n_rows;
+
+	for (n = 0; n < 32; n++) {
+		if (sequence[n]) {
+			t = &track[192 * (sequence[n] - 1)];
+			for (o = 0; o < n_rows; o++) {
+				if (t[0] || t[1] || t[2]) pattern->n_entries++;
+				t += 3;
+			}
+		}
+	}
+
+	entry = malloc(pattern->n_entries * sizeof(*entry));
+	if (!entry) return -1;
+	pattern->entry = entry;
+
+	for (n = 0; n < n_rows; n++) {
+		for (o = 0; o < 32; o++) {
+			if (sequence[o]) {
+				t = &track[192 * (sequence[o] - 1) + (n * 3)];
+				if (t[0] || t[1] || t[2]) {
+					entry->channel = o;
+					entry->mask = 0;
+					note = t[0] >> 2;
+					sample = ((t[0] << 4) | (t[1] >> 4)) & 0x3F;
+
+					if (note) {
+						entry->mask |= IT_ENTRY_NOTE;
+						entry->note = note + 24;
+					}
+
+					if (sample) {
+						entry->mask |= IT_ENTRY_INSTRUMENT;
+						entry->instrument = sample;
+					}
+
+					_dumb_it_xm_convert_effect(t[1] & 0xF, t[2], entry, 1);
+
+					if (entry->mask) entry++;
+				}
+			}
+		}
+		IT_SET_END_ROW(entry);
+		entry++;
+	}
+
+	pattern->n_entries = entry - pattern->entry;
+
+	return 0;
+}
+
+static int it_mtm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	int finetune, flags;
+
+	dumbfile_getnc(sample->name, 22, f);
+	sample->name[22] = 0;
+
+	sample->filename[0] = 0;
+
+	sample->length = dumbfile_igetl(f);
+	sample->loop_start = dumbfile_igetl(f);
+	sample->loop_end = dumbfile_igetl(f);
+	finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */
+	sample->global_volume = 64;
+	sample->default_volume = dumbfile_getc(f);
+
+	flags = dumbfile_getc(f);
+
+	if (sample->length <= 0) {
+		sample->flags = 0;
+		return 0;
+	}
+
+	sample->flags = IT_SAMPLE_EXISTS;
+
+	if (flags & 1) {
+		sample->flags |= IT_SAMPLE_16BIT;
+		sample->length >>= 1;
+		sample->loop_start >>= 1;
+		sample->loop_end >>= 1;
+	}
+
+	sample->default_pan = 0;
+	sample->C5_speed = (long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
+	// the above line might be wrong
+
+	if (sample->loop_end > sample->length)
+		sample->loop_end = sample->length;
+
+	if (sample->loop_end - sample->loop_start > 2)
+		sample->flags |= IT_SAMPLE_LOOP;
+
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = 0; // do we have to set _all_ these?
+	sample->max_resampling_quality = -1;
+
+	return dumbfile_error(f);
+}
+
+static int it_mtm_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	long i;
+	long truncated_size;
+
+	/* let's get rid of the sample data coming after the end of the loop */
+	if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
+		truncated_size = sample->length - sample->loop_end;
+		sample->length = sample->loop_end;
+	} else {
+		truncated_size = 0;
+	}
+
+	sample->left = malloc(sample->length);
+
+	if (!sample->left)
+		return -1;
+
+	for (i = 0; i < sample->length; i++)
+		((signed char *)sample->left)[i] = dumbfile_getc(f) ^ 0x80;
+
+	dumbfile_skip(f, truncated_size);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	return 0;
+}
+
+static DUMB_IT_SIGDATA *it_mtm_load_sigdata(DUMBFILE *f, int * version)
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	int n, o, n_tracks, l_comment, n_rows, n_channels;
+
+	unsigned char * track;
+
+	unsigned short * sequence;
+
+	char * comment;
+
+	if (dumbfile_getc(f) != 'M' ||
+		dumbfile_getc(f) != 'T' ||
+		dumbfile_getc(f) != 'M') goto error;
+
+	*version = dumbfile_getc(f);
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) goto error;
+
+	dumbfile_getnc(sigdata->name, 20, f);
+	sigdata->name[20] = 0;
+
+	n_tracks = dumbfile_igetw(f);
+	sigdata->n_patterns = dumbfile_getc(f) + 1;
+	sigdata->n_orders = dumbfile_getc(f) + 1;
+	l_comment = dumbfile_igetw(f);
+	sigdata->n_samples = dumbfile_getc(f);
+	//if (dumbfile_getc(f)) goto error_sd;
+	dumbfile_getc(f);
+	n_rows = dumbfile_getc(f);
+	n_channels = dumbfile_getc(f);
+
+	if (dumbfile_error(f) ||
+		(n_tracks <= 0) ||
+		(sigdata->n_samples <= 0) ||
+		(n_rows <= 0 || n_rows > 64) ||
+		(n_channels <= 0 || n_channels > 32)) goto error_sd;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	if (dumbfile_getnc(sigdata->channel_pan, 32, f) < 32) goto error_sd;
+
+	for (n = 0; n < 32; n++) {
+		if (sigdata->channel_pan[n] <= 15) {
+			sigdata->channel_pan[n] -= (sigdata->channel_pan[n] & 8) >> 3;
+			sigdata->channel_pan[n] = (sigdata->channel_pan[n] * 32) / 7;
+		} else {
+			sigdata->channel_volume[n] = 0;
+			sigdata->channel_pan[n] = 7;
+		}
+	}
+
+	for (n = 32; n < DUMB_IT_N_CHANNELS; n += 4) {
+		sigdata->channel_pan[n  ] = 16;
+		sigdata->channel_pan[n+1] = 48;
+		sigdata->channel_pan[n+2] = 48;
+		sigdata->channel_pan[n+3] = 16;
+	}
+
+	sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+	if (!sigdata->sample) goto error_sd;
+
+	sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+	sigdata->global_volume = 128;
+	sigdata->mixing_volume = 48;
+	sigdata->speed = 6;
+	sigdata->tempo = 125;
+	sigdata->pan_separation = 128;
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_instruments = 0;
+
+	sigdata->restart_position = 0;
+	sigdata->n_pchannels = n_channels;
+
+	for (n = 0; n < sigdata->n_samples; n++)
+		sigdata->sample[n].right = sigdata->sample[n].left = NULL;
+
+	for (n = 0; n < sigdata->n_samples; n++) {
+		if (it_mtm_read_sample_header(&sigdata->sample[n], f)) goto error_usd;
+	}
+
+	sigdata->order = malloc(sigdata->n_orders);
+	if (!sigdata->order) goto error_usd;
+
+	if (dumbfile_getnc(sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_usd;
+	if (sigdata->n_orders < 128)
+		if (dumbfile_skip(f, 128 - sigdata->n_orders)) goto error_usd;
+
+	track = malloc(192 * n_tracks);
+	if (!track) goto error_usd;
+
+	if (dumbfile_getnc(track, 192 * n_tracks, f) < 192 * n_tracks) goto error_ft;
+
+	sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+	if (!sigdata->pattern) goto error_ft;
+	for (n = 0; n < sigdata->n_patterns; n++)
+		sigdata->pattern[n].entry = NULL;
+
+	sequence = malloc(sigdata->n_patterns * 32 * sizeof(*sequence));
+	if (!sequence) goto error_ft;
+
+	for (n = 0; n < sigdata->n_patterns; n++) {
+		for (o = 0; o < 32; o++) {
+			sequence[(n * 32) + o] = dumbfile_igetw(f);
+			if (sequence[(n * 32) + o] > n_tracks)
+			{
+				//goto error_fs;
+				// illegal track number, silence instead of rejecting the file
+				sequence[(n * 32) + o] = 0;
+			}
+		}
+	}
+
+	for (n = 0; n < sigdata->n_patterns; n++) {
+		if (it_mtm_assemble_pattern(&sigdata->pattern[n], track, &sequence[n * 32], n_rows)) goto error_fs;
+	}
+
+	if (l_comment) {
+		comment = malloc(l_comment);
+		if (!comment) goto error_fs;
+		if (dumbfile_getnc(comment, l_comment, f) < l_comment) goto error_fc;
+
+		/* Time for annoying "logic", yes. We want each line which has text,
+		 * and each blank line in between all the valid lines.
+		 */
+
+		/* Find last actual line. */
+		for (o = -1, n = 0; n < l_comment; n += 40) {
+			if (comment[n]) o = n;
+		}
+
+		if (o >= 0) {
+
+			int l, m;
+
+			for (l = 0, n = 0; n <= o; n += 40) {
+				l += strlen_max(&comment[n], 40) + 2;
+			}
+
+			l -= 1;
+
+			sigdata->song_message = malloc(l);
+			if (!sigdata->song_message) goto error_fc;
+
+			for (m = 0, n = 0; n <= o; n += 40) {
+				int p = strlen_max(&comment[n], 40);
+				if (p) {
+					memcpy(sigdata->song_message + m, &comment[n], p);
+					m += p;
+				}
+				if (l - m > 1) {
+					sigdata->song_message[m++] = 13;
+					sigdata->song_message[m++] = 10;
+				}
+			}
+			
+			sigdata->song_message[m] = 0;
+		}
+
+		free(comment);
+	}
+
+	for (n = 0; n < sigdata->n_samples; n++) {
+		if (it_mtm_read_sample_data(&sigdata->sample[n], f)) goto error_fs;
+	}
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	free(sequence);
+	free(track);
+
+	return sigdata;
+
+error_fc:
+	free(comment);
+error_fs:
+	free(sequence);
+error_ft:
+	free(track);
+error_usd:
+	_dumb_it_unload_sigdata(sigdata);
+	return NULL;
+
+error_sd:
+	free(sigdata);
+error:
+	return NULL;
+}
+
+static char hexdigit(int in)
+{
+	if (in < 10) return in + '0';
+	else return in + 'A' - 10;
+}
+
+DUH *dumb_read_mtm(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+	int ver;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_mtm_load_sigdata(f, &ver);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		char version[16];
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		version[0] = 'M';
+		version[1] = 'T';
+		version[2] = 'M';
+		version[3] = ' ';
+		version[4] = 'v';
+		version[5] = hexdigit(ver >> 4);
+		version[6] = '.';
+		version[7] = hexdigit(ver & 15);
+		version[8] = 0;
+		tag[1][1] = (const char *) &version;
+		return make_duh(length, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readoldpsm.c
@@ -1,0 +1,728 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readpsm.c - Code to read an old Protracker         / / \  \
+ *             Studio module from an open file.      | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller.                                  \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+static int psm_sample_compare(const void *e1, const void *e2)
+{
+	const unsigned char * pa = e1;
+	const unsigned char * pb = e2;
+	int a = pa[37] | (pa[38] << 8) | (pa[39] << 16) | (pa[40] << 24);
+	int b = pb[37] | (pb[38] << 8) | (pb[39] << 16) | (pb[40] << 24);
+	return a - b;
+}
+
+static int it_old_psm_read_samples(IT_SAMPLE ** sample, DUMBFILE * f, int * num, const unsigned char * prebuffer, long data_pos, long data_size)
+{
+	int n, o, pos, count = *num, true_num, snum, offset, flags, finetune, delta;
+
+	unsigned char * buffer, * sbuffer = 0;
+	const unsigned char * sdata;
+
+	buffer = malloc(count * 64);
+	if (!buffer) goto error;
+
+	if (dumbfile_getnc(buffer, count * 64, f) < count * 64) goto error_fb;
+
+	pos = dumbfile_pos(f);
+
+	true_num = 0;
+
+	for (n = 0; n < count; n++) {
+		snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8);
+		if ((snum < 1) || (snum > 255)) goto error_fb;
+		if (true_num < snum) true_num = snum;
+	}
+
+	if (true_num > count) {
+		IT_SAMPLE * meh = realloc(*sample, true_num * sizeof(*meh));
+		if (!meh) goto error_fb;
+		for (n = count; n < true_num; n++) {
+			meh[n].right = meh[n].left = NULL;
+		}
+		*sample = meh;
+		*num = true_num;
+	}
+
+	qsort(buffer, count, 64, &psm_sample_compare);
+
+	for (n = 0; n < true_num; n++) {
+		(*sample)[n].flags = 0;
+	}
+
+	for (n = 0; n < count; n++) {
+		IT_SAMPLE * s;
+		snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8);
+		s = &((*sample)[snum - 1]);
+		memcpy(s->filename, buffer + (n * 64), 13);
+		s->filename[13] = 0;
+		memcpy(s->name, buffer + (n * 64) + 13, 24);
+		s->name[24] = 0;
+		offset = buffer[(n * 64) + 37] | (buffer[(n * 64) + 38] << 8) |
+				 (buffer[(n * 64) + 39] << 16) | (buffer[(n * 64) + 40] << 24);
+		flags = buffer[(n * 64) + 47];
+		s->length = buffer[(n * 64) + 48] | (buffer[(n * 64) + 49] << 8) |
+					(buffer[(n * 64) + 50] << 16) | (buffer[(n * 64) + 51] << 24);
+		s->loop_start = buffer[(n * 64) + 52] | (buffer[(n * 64) + 53] << 8) |
+						(buffer[(n * 64) + 54] << 16) | (buffer[(n * 64) + 55] << 24);
+		s->loop_end = buffer[(n * 64) + 56] | (buffer[(n * 64) + 57] << 8) |
+					  (buffer[(n * 64) + 58] << 16) | (buffer[(n * 64) + 59] << 24);
+
+		if (s->length <= 0) continue;
+
+		finetune = buffer[(n * 64) + 60];
+		s->default_volume = buffer[(n * 64) + 61];
+		s->C5_speed = buffer[(n * 64) + 62] | (buffer[(n * 64) + 63] << 8);
+		if (finetune < 16) {
+			if (finetune >= 8) finetune -= 16;
+			s->C5_speed = (long)((double)s->C5_speed * pow(DUMB_PITCH_BASE, finetune*32));
+		}
+
+		s->flags |= IT_SAMPLE_EXISTS;
+		if (flags & 0x41) {
+			s->flags &= ~IT_SAMPLE_EXISTS;
+			continue;
+		}
+		if (flags & 0x20) s->flags |= IT_SAMPLE_PINGPONG_LOOP;
+		if (flags & 4) s->flags |= IT_SAMPLE_16BIT;
+
+		if (flags & 0x80) {
+			s->flags |= IT_SAMPLE_LOOP;
+			if ((unsigned int)s->loop_end > (unsigned int)s->length)
+				s->loop_end = s->length;
+			else if ((unsigned int)s->loop_start >= (unsigned int)s->loop_end)
+				s->flags &= ~IT_SAMPLE_LOOP;
+			else
+				s->length = s->loop_end;
+		}
+
+		s->global_volume = 64;
+
+		s->vibrato_speed = 0;
+		s->vibrato_depth = 0;
+		s->vibrato_rate = 0;
+		s->vibrato_waveform = IT_VIBRATO_SINE;
+		s->max_resampling_quality = -1;
+
+		s->left = malloc(s->length * ((flags & 4) ? 2 : 1));
+		if (!s->left) goto error_fb;
+
+		if ((offset >= data_pos) &&
+			((offset + s->length * ((flags & 4) ? 2 : 1)) <= (data_pos + data_size))) {
+			sdata = prebuffer + offset - data_pos;
+		} else if (offset >= pos) {
+			if (dumbfile_skip(f, offset - pos)) goto error_fb;
+			pos = offset;
+			offset = s->length * ((flags & 4) ? 2 : 1);
+			sbuffer = malloc(offset);
+			if (!sbuffer) goto error_fb;
+			if (dumbfile_getnc(sbuffer, offset, f) < offset) goto error_fsb;
+			sdata = sbuffer;
+		} else
+			goto error_fb;
+
+		if (flags & 0x10) {
+			if (flags & 8) {
+				if (flags & 4) {
+					for (o = 0; o < s->length; o++)
+						((short *)s->left)[o] = (sdata[o * 2] | (sdata[(o * 2) + 1] << 8)) ^ 0x8000;
+				} else {
+					for (o = 0; o < s->length; o++)
+						((signed char *)s->left)[o] = sdata[o] ^ 0x80;
+				}
+			} else {
+				if (flags & 4) {
+					for (o = 0; o < s->length; o++)
+						((short *)s->left)[o] = sdata[o * 2] | (sdata[(o * 2) + 1] << 8);
+				} else {
+					memcpy(s->left, sdata, s->length);
+				}
+			}
+		} else {
+			delta = 0;
+			if (flags & 8) {
+				/* unsigned delta? mehhh, does anything even use this? */
+				if (flags & 4) {
+					for (o = 0; o < s->length; o++) {
+						delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8));
+						((short *)s->left)[o] = delta ^ 0x8000;
+					}
+				} else {
+					for (o = 0; o < s->length; o++) {
+						delta += (signed char)sdata[o];
+						((signed char *)s->left)[o] = delta ^ 0x80;
+					}
+				}
+			} else {
+				if (flags & 4) {
+					for (o = 0; o < s->length; o++) {
+						delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8));
+						((short *)s->left)[o] = delta;
+					}
+				} else {
+					for (o = 0; o < s->length; o++) {
+						delta += (signed char)sdata[o];
+						((signed char *)s->left)[o] = delta;
+					}
+				}
+			}
+		}
+
+		if (sbuffer) {
+			free(sbuffer);
+			sbuffer = 0;
+		}
+	}
+
+	free(buffer);
+
+	return 0;
+
+error_fsb:
+	if (sbuffer) free(sbuffer);
+error_fb:
+	free(buffer);
+error:
+	return -1;
+}
+
+static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num, int size, int pchans, int sflags)
+{
+	int n, offset, psize, rows, chans, row, flags, channel;
+
+	unsigned char * buffer, * ptr, * end;
+
+	IT_ENTRY * entry;
+
+	buffer = malloc(size);
+	if (!buffer) goto error;
+
+	if (dumbfile_getnc(buffer, size, f) < size) goto error_fb;
+
+	offset = 0;
+
+	for (n = 0; n < num; n++) {
+		IT_PATTERN * p = &pattern[n];
+
+		if (offset >= size) goto error_fb;
+
+		ptr = buffer + offset;
+		psize = ptr[0] | (ptr[1] << 8);
+		rows = ptr[2];
+		chans = ptr[3];
+
+		if (!rows || !chans) {
+			p->n_rows = 1;
+			p->n_entries = 0;
+			continue;
+		}
+
+		psize = (psize + 15) & ~15;
+
+		end = ptr + psize;
+		ptr += 4;
+
+		p->n_rows = rows;
+		p->n_entries = rows;
+		row = 0;
+
+		while ((row < rows) && (ptr < end)) {
+			flags = *ptr++;
+			if (!flags) {
+				row++;
+				continue;
+			}
+			if (flags & 0xE0) {
+				p->n_entries++;
+				if (flags & 0x80) ptr += 2;
+				if (flags & 0x40) ptr++;
+				if (flags & 0x20) {
+					ptr++;
+					if (*ptr == 40) ptr += 3;
+					else ptr++;
+				}
+			}
+		}
+
+		entry = malloc(p->n_entries * sizeof(*p->entry));
+		if (!entry) goto error_fb;
+
+		p->entry = entry;
+
+		ptr = buffer + offset + 4;
+		row = 0;
+
+		while ((row < rows) && (ptr < end)) {
+			flags = *ptr++;
+			if (!flags) {
+				IT_SET_END_ROW(entry);
+				entry++;
+				row++;
+				continue;
+			}
+			if (flags & 0xE0) {
+				entry->mask = 0;
+				entry->channel = channel = flags & 0x1F;
+				if (channel >= chans)
+				{
+					//channel = 0;
+					goto error_fb;
+				}
+				if (flags & 0x80) {
+					if ((*ptr < 60) && (channel < pchans)) {
+						entry->mask |= IT_ENTRY_NOTE;
+						entry->note = *ptr + 36;
+					}
+					ptr++;
+					if (*ptr) {
+						entry->mask |= IT_ENTRY_INSTRUMENT;
+						entry->instrument = *ptr;
+					}
+					ptr++;
+				}
+				if (flags & 0x40) {
+					if (*ptr <= 64) {
+						entry->mask |= IT_ENTRY_VOLPAN;
+						entry->volpan = *ptr;
+					}
+					ptr++;
+				}
+				if (flags & 0x20) {
+					entry->mask |= IT_ENTRY_EFFECT;
+
+					switch (*ptr) {
+						case 1:
+							entry->effect = IT_XM_FINE_VOLSLIDE_UP;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 2:
+							entry->effect = IT_VOLUME_SLIDE;
+							entry->effectvalue = (ptr[1] << 4) & 0xF0;
+							break;
+
+						case 3:
+							entry->effect = IT_XM_FINE_VOLSLIDE_DOWN;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 4:
+							entry->effect = IT_VOLUME_SLIDE;
+							entry->effectvalue = ptr[1] & 0xF;
+							break;
+
+						case 10:
+							entry->effect = IT_PORTAMENTO_UP;
+							entry->effectvalue = EFFECT_VALUE(0xF, ptr[1]);
+							break;
+
+						case 11:
+							entry->effect = IT_PORTAMENTO_UP;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 12:
+							entry->effect = IT_PORTAMENTO_DOWN;
+							entry->effectvalue = EFFECT_VALUE(ptr[1], 0xF);
+							break;
+
+						case 13:
+							entry->effect = IT_PORTAMENTO_DOWN;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 14:
+							entry->effect = IT_TONE_PORTAMENTO;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 15:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_SET_GLISSANDO_CONTROL, ptr[1] & 15);
+							break;
+
+						case 16:
+							entry->effect = IT_VOLSLIDE_TONEPORTA;
+							entry->effectvalue = ptr[1] << 4;
+							break;
+
+						case 17:
+							entry->effect = IT_VOLSLIDE_TONEPORTA;
+							entry->effectvalue = ptr[1] & 0xF;
+							break;
+
+						case 20:
+							entry->effect = IT_VIBRATO;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 21:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_SET_VIBRATO_WAVEFORM, ptr[1] & 11);
+							break;
+
+						case 22:
+							entry->effect = IT_VOLSLIDE_VIBRATO;
+							entry->effectvalue = ptr[1] << 4;
+							break;
+
+						case 23:
+							entry->effect = IT_VOLSLIDE_VIBRATO;
+							entry->effectvalue = ptr[1] & 0xF;
+							break;
+
+						case 30:
+							entry->effect = IT_TREMOLO;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 31:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_SET_TREMOLO_WAVEFORM, ptr[1] & 11);
+							break;
+
+						case 40:
+							entry->effect = IT_SET_SAMPLE_OFFSET;
+							entry->effectvalue = ptr[2];
+							ptr += 2;
+							break;
+
+						case 41:
+							entry->effect = IT_XM_RETRIGGER_NOTE;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 42:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_DELAYED_NOTE_CUT, ptr[1] & 0xF);
+							break;
+
+						case 43:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_NOTE_DELAY, ptr[1] & 0xF);
+							break;
+
+						case 50:
+							entry->effect = IT_JUMP_TO_ORDER;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 51:
+							entry->effect = IT_BREAK_TO_ROW;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 52:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_LOOP, ptr[1] & 0xF);
+							break;
+
+						case 53:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_DELAY, ptr[1] & 0xF);
+							break;
+
+						case 60:
+							entry->effect = IT_SET_SPEED;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 61:
+							entry->effect = IT_SET_SONG_TEMPO;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 70:
+							entry->effect = IT_ARPEGGIO;
+							entry->effectvalue = ptr[1];
+							break;
+
+						case 71:
+							entry->effect = IT_S;
+							entry->effectvalue = EFFECT_VALUE(IT_S_FINETUNE, ptr[1] & 0xF);
+							break;
+
+						case 72:
+							/* "balance" ... panning? */
+							entry->effect = IT_SET_PANNING;
+							entry->effectvalue = ((ptr[1] - ((ptr[1] & 8) >> 3)) << 5) / 7;
+							break;
+
+						default:
+							entry->mask &= ~IT_ENTRY_EFFECT;
+					}
+
+					ptr += 2;
+				}
+				if (entry->mask) entry++;
+			}
+		}
+
+		p->n_entries = entry - p->entry;
+		offset += psize;
+	}
+
+	free(buffer);
+
+	return 0;
+
+error_fb:
+	free(buffer);
+error:
+	return -1;
+}
+
+#define PSM_COMPONENT_ORDERS            0
+#define PSM_COMPONENT_PANPOS            1
+#define PSM_COMPONENT_PATTERNS          2
+#define PSM_COMPONENT_SAMPLE_HEADERS    3
+#define PSM_COMPONENT_COMMENTS          4
+
+typedef struct PSM_COMPONENT
+{
+	unsigned char type;
+	long offset;
+}
+PSM_COMPONENT;
+
+static int psm_component_compare(const void *e1, const void *e2)
+{
+	return ((const PSM_COMPONENT *)e1)->offset -
+	       ((const PSM_COMPONENT *)e2)->offset;
+}
+
+static DUMB_IT_SIGDATA *it_old_psm_load_sigdata(DUMBFILE *f)
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	unsigned char * ptr = 0;
+
+	PSM_COMPONENT *component;
+	int n_components = 0;
+
+	int n, flags, version, pver, n_orders, n_channels, total_pattern_size;
+
+	if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',254)) goto error;
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) goto error;
+
+	if (dumbfile_getnc(sigdata->name, 60, f) < 60 ||
+		sigdata->name[59] != 0x1A) goto error_sd;
+	sigdata->name[59] = 0;
+
+	flags = dumbfile_getc(f);
+	version = dumbfile_getc(f);
+	pver = dumbfile_getc(f);
+	sigdata->speed = dumbfile_getc(f);
+	sigdata->tempo = dumbfile_getc(f);
+	sigdata->mixing_volume = dumbfile_getc(f);
+	sigdata->n_orders = dumbfile_igetw(f);
+	n_orders = dumbfile_igetw(f);
+	sigdata->n_patterns = dumbfile_igetw(f);
+	sigdata->n_samples = dumbfile_igetw(f);
+	sigdata->n_pchannels = dumbfile_igetw(f);
+	n_channels = dumbfile_igetw(f);
+
+	if (dumbfile_error(f) ||
+		(flags & 1) ||
+		(version != 1 && version != 0x10) ||
+		(pver) ||
+		(sigdata->n_orders <= 0) ||
+		(sigdata->n_orders > 255) ||
+		(n_orders > 255) ||
+		(n_orders < sigdata->n_orders) ||
+		(sigdata->n_patterns > 255) ||
+		(sigdata->n_samples > 255) ||
+		(sigdata->n_pchannels > DUMB_IT_N_CHANNELS) ||
+		(sigdata->n_pchannels > n_channels) ||
+		(n_channels > DUMB_IT_N_CHANNELS))
+		goto error_sd;
+
+	sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+	sigdata->global_volume = 128;
+	sigdata->pan_separation = 128;
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_instruments = 0;
+
+	sigdata->restart_position = 0;
+
+	sigdata->order = malloc(sigdata->n_orders);
+	if (!sigdata->order) goto error_usd;
+
+	if (sigdata->n_samples) {
+		sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+		if (!sigdata->sample) goto error_usd;
+		for (n = 0; n < sigdata->n_samples; n++)
+			sigdata->sample[n].right = sigdata->sample[n].left = NULL;
+	}
+
+	if (sigdata->n_patterns) {
+		sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+		if (!sigdata->pattern) goto error_usd;
+		for (n = 0; n < sigdata->n_patterns; n++)
+			sigdata->pattern[n].entry = NULL;
+	}
+
+	component = malloc(5 * sizeof(*component));
+	if (!component) goto error_usd;
+
+	for (n = 0; n < 5; n++) {
+		component[n_components].offset = dumbfile_igetl(f);
+		if (component[n_components].offset) {
+			component[n_components].type = n;
+			n_components++;
+		}
+	}
+
+	if (!n_components) goto error_fc;
+
+	total_pattern_size = dumbfile_igetl(f);
+	if (!total_pattern_size) goto error_fc;
+
+	qsort(component, n_components, sizeof(PSM_COMPONENT), &psm_component_compare);
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+		sigdata->channel_pan[n  ] = 16;
+		sigdata->channel_pan[n+1] = 48;
+		sigdata->channel_pan[n+2] = 48;
+		sigdata->channel_pan[n+3] = 16;
+	}
+
+	for (n = 0; n < n_components; n++)
+	{
+		int o;
+		long data_pos, data_size;
+
+		/* Whee, sample data may be before the sample headers */
+
+		data_pos = dumbfile_pos(f);
+		if (data_pos > component[n].offset) goto error_fc;
+
+		data_size = component[n].offset - data_pos;
+
+		if (data_size) {
+			ptr = malloc(data_size);
+			if (!ptr) goto error_fc;
+
+			if (dumbfile_getnc(ptr, data_size, f) < data_size) goto error_fp;
+		}
+
+		switch (component[n].type) {
+
+			case PSM_COMPONENT_ORDERS:
+				if (dumbfile_getnc(sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_fp;
+				if (n_orders > sigdata->n_orders)
+					if (dumbfile_skip(f, n_orders - sigdata->n_orders))
+						goto error_fp;
+				if (dumbfile_igetw(f)) goto error_fp;
+				break;
+
+			case PSM_COMPONENT_PANPOS:
+				if (dumbfile_getnc(sigdata->channel_pan, sigdata->n_pchannels, f) < sigdata->n_pchannels) goto error_fp;
+				for (o = 0; o < sigdata->n_pchannels; o++) {
+					sigdata->channel_pan[o] -= (sigdata->channel_pan[o] & 8) >> 3;
+					sigdata->channel_pan[o] = ((int)sigdata->channel_pan[o] << 5) / 7;
+				}
+				break;
+
+			case PSM_COMPONENT_PATTERNS:
+				if (it_old_psm_read_patterns(sigdata->pattern, f, sigdata->n_patterns, total_pattern_size, sigdata->n_pchannels, flags)) goto error_fp;
+				break;
+
+			case PSM_COMPONENT_SAMPLE_HEADERS:
+				if (it_old_psm_read_samples(&sigdata->sample, f, &sigdata->n_samples, ptr, data_pos, data_size)) goto error_fp;
+				break;
+
+			case PSM_COMPONENT_COMMENTS:
+				if (dumbfile_mgetl(f) == DUMB_ID('T','E','X','T')) {
+					o = dumbfile_igetw(f);
+					if (o > 0) {
+						sigdata->song_message = malloc(o + 1);
+						if (dumbfile_getnc(sigdata->song_message, o, f) < o) goto error_fp;
+						sigdata->song_message[o] = 0;
+					}
+				}
+				break;
+		}
+
+		if (ptr) {
+			free(ptr);
+			ptr = 0;
+		}
+	}
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	free(component);
+
+	return sigdata;
+
+error_fp:
+	if (ptr) free(ptr);
+error_fc:
+	free(component);
+error_usd:
+	_dumb_it_unload_sigdata(sigdata);
+	return NULL;
+error_sd:
+	free(sigdata);
+error:
+	return NULL;
+}
+
+DUH *dumb_read_old_psm(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_old_psm_load_sigdata(f);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0; /*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "PSM (old)";
+		return make_duh(length, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readpsm.c
@@ -1,0 +1,1217 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readpsm.c - Code to read a Protracker Studio       / / \  \
+ *             module from an open file.             | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller.                                  \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+#define PSMV_OLD 940730
+#define PSMV_NEW 940902
+
+typedef struct _PSMCHUNK
+{
+	int id;
+	int len;
+	unsigned char * data;
+} PSMCHUNK;
+
+typedef struct _PSMEVENT
+{
+	int type;
+	unsigned char data[8];
+} PSMEVENT;
+
+#define PSM_EVENT_END               0
+#define PSM_EVENT_PLAY_PATTERN      1
+#define PSM_EVENT_JUMP_TO_LINE      4
+#define PSM_EVENT_SET_SPEED         7
+#define PSM_EVENT_SET_BPM           8
+#define PSM_EVENT_SAMPLE_MAP_TABLE 12
+#define PSM_EVENT_CHANGE_PAN       13
+#define PSM_EVENT_CHANGE_VOL       14
+
+static int it_psm_process_sample(IT_SAMPLE * sample, const unsigned char * data, int len, int id, int version) {
+	int flags;
+	int insno;
+	int length;
+	int loopstart;
+	int loopend;
+	int panpos;
+	int defvol;
+	int samplerate;
+
+	if (len < 0x60) return -1;
+
+	flags = data[0];
+
+	if (version == PSMV_OLD) {
+		memcpy(sample->name, data + 0x0D, 34);
+		sample->name[34] = 0;
+
+		insno = data[0x34] | (data[0x35] << 8);
+		length = data[0x36] | (data[0x37] << 8) | (data[0x38] << 16) | (data[0x39] << 24);
+		loopstart = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24);
+		loopend = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24);
+		panpos = data[0x43];
+		defvol = data[0x44];
+		samplerate = data[0x49] | (data[0x4A] << 8) | (data[0x4B] << 16) | (data[0x4C] << 24);
+	} else if (version == PSMV_NEW) {
+		memcpy(sample->name, data + 0x11, 34);
+		sample->name[34] = 0;
+
+		insno = data[0x38] | (data[0x39] << 8);
+		length = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24);
+		loopstart = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24);
+		loopend = data[0x42] | (data[0x43] << 8) | (data[0x44] << 16) | (data[0x45] << 24);
+		panpos = data[0x48];
+		defvol = data[0x49];
+		samplerate = data[0x4E] | (data[0x4F] << 8) | (data[0x50] << 16) | (data[0x51] << 24);
+	}
+
+	if (insno != id) return -1;
+
+	if (!length) {
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+		return 0;
+	}
+	
+	if ((length > len - 0x60) || ((flags & 0x7F) != 0)) return -1;
+
+	sample->flags = IT_SAMPLE_EXISTS;
+	sample->length = length;
+	sample->loop_start = loopstart;
+	sample->loop_end = loopend;
+	sample->C5_speed = samplerate;
+	sample->default_volume = defvol >> 1;
+	sample->default_pan = 0;
+	sample->filename[0] = 0;
+	sample->global_volume = 64;
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = IT_VIBRATO_SINE;
+	sample->max_resampling_quality = -1;
+
+	if (flags & 0x80) {
+		if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) &&
+			((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) {
+			sample->length = sample->loop_end;
+			sample->flags |= IT_SAMPLE_LOOP;
+		}
+	}
+
+	sample->left = malloc(sample->length);
+	if (!sample->left)
+		return -1;
+
+	flags = 0;
+	data += 0x60;
+
+	for (insno = 0; insno < sample->length; insno++) {
+		flags += (signed char)(*data++);
+		((signed char *)sample->left)[insno] = flags;
+	}
+
+	return 0;
+}
+
+static int it_psm_process_pattern(IT_PATTERN * pattern, const unsigned char * data, int len, int speed, int bpm, const unsigned char * pan, const int * vol, int version) {
+	int length, nrows, row, rowlen, pos;
+	unsigned flags, chan;
+	IT_ENTRY * entry;
+
+	length = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+	if (len > length) len = length;
+
+	if (version == PSMV_OLD) {
+		if (len < 10) return -1;
+		data += 8;
+		len -= 8;
+	} else if (version == PSMV_NEW) {
+		if (len < 14) return -1;
+		data += 12;
+		len -= 12;
+	}
+
+	nrows = data[0] | (data[1] << 8);
+
+	if (!nrows) return 0;
+
+	pattern->n_rows = nrows;
+
+	data += 2;
+	len -= 2;
+
+	pattern->n_entries = 0;
+
+	row = 0;
+	pos = 2;
+	rowlen = data[0] | (data[1] << 8);
+
+	while ((row < nrows) && (pos < len)) {
+		if (pos >= rowlen) {
+			row++;
+			rowlen += data[pos] | (data[pos+1] << 8);
+			pos += 2;
+			continue;
+		}
+
+		flags = data[pos++];
+		chan = data[pos++];
+
+		if (chan > 63) return -1;
+
+		if (flags & 0xF0) {
+			pattern->n_entries++;
+			if (flags & 0x80) pos++;
+			if (flags & 0x40) pos++;
+			if (flags & 0x20) pos++;
+			if (flags & 0x10) {
+				switch (data[pos]) {
+					case 0x29:
+						pos++;
+					case 0x33:
+						pos++;
+					default:
+						pos += 2;
+				}
+			}
+		}
+	}
+
+	if (!pattern->n_entries) return 0;
+
+	pattern->n_entries += nrows;
+	if (speed) pattern->n_entries++;
+	if (bpm >= 0x20) pattern->n_entries++;
+
+	for (pos = 0; pos < 32; pos++) {
+		if (!(pan[pos*2+1] & 0xF9)) pattern->n_entries++;
+		if (vol[pos] != -1) pattern->n_entries++;
+	}
+
+	pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+	if (!pattern->entry) return -1;
+
+	entry = pattern->entry;
+
+	if (speed) {
+		entry->channel = 0;
+		entry->mask = IT_ENTRY_EFFECT;
+		entry->effect = IT_SET_SPEED;
+		entry->effectvalue = speed;
+		entry++;
+	}
+
+	if (bpm >= 0x20) {
+		entry->channel = 0;
+		entry->mask = IT_ENTRY_EFFECT;
+		entry->effect = IT_SET_SONG_TEMPO;
+		entry->effectvalue = bpm;
+		entry++;
+	}
+
+	for (pos = 0; pos < 32; pos++) {
+		if (!(pan[pos*2+1] & 0xF9)) {
+			entry->channel = pos;
+			entry->mask = IT_ENTRY_EFFECT;
+			switch (pan[pos*2+1]) {
+			case 0:
+				entry->effect = IT_SET_PANNING;
+				entry->effectvalue = pan[pos*2] ^ 128;
+				break;
+			case 2:
+				entry->effect = IT_S;
+				entry->effectvalue = EFFECT_VALUE(IT_S_SET_SURROUND_SOUND,1);
+				break;
+			case 4:
+				entry->effect = IT_SET_PANNING;
+				entry->effectvalue = 128;
+				break;
+			}
+			entry++;
+		}
+		if (vol[pos] != -1) {
+			entry->channel = pos;
+			entry->mask = IT_ENTRY_EFFECT;
+			entry->effect = IT_SET_CHANNEL_VOLUME;
+			entry->effectvalue = (vol[pos] + 2) >> 2;
+			entry++;
+		}
+	}
+
+	row = 0;
+	pos = 2;
+	rowlen = data[0] | (data[1] << 8);
+
+	while ((row < nrows) && (pos < len)) {
+		if (pos >= rowlen) {
+			IT_SET_END_ROW(entry);
+			entry++;
+			row++;
+			rowlen += data[pos] | (data[pos+1] << 8);
+			pos += 2;
+			continue;
+		}
+
+		flags = data[pos++];
+		entry->channel = data[pos++];
+		entry->mask = 0;
+
+		if (flags & 0xF0) {
+			if (flags & 0x80) {
+				entry->mask |= IT_ENTRY_NOTE;
+				if (version == PSMV_OLD) {
+					if ((data[pos] < 0x80)) entry->note = (data[pos]>>4)*12+(data[pos]&0x0f)+12;
+					else entry->mask &= ~IT_ENTRY_NOTE;
+				} else if (version == PSMV_NEW) {
+					if ((data[pos]) && (data[pos] < 84)) entry->note = data[pos] + 35;
+					else entry->mask &= ~IT_ENTRY_NOTE;
+				}
+				pos++;
+			}
+
+			if (flags & 0x40) {
+				entry->mask |= IT_ENTRY_INSTRUMENT;
+				entry->instrument = data[pos++] + 1;
+			}
+
+			if (flags & 0x20) {
+				entry->mask |= IT_ENTRY_VOLPAN;
+				entry->volpan = (data[pos++] + 1) >> 1;
+			}
+
+			if (flags & 0x10) {
+				entry->mask |= IT_ENTRY_EFFECT;
+				length = data[pos+1];
+				switch (data[pos]) {
+					case 1:
+						entry->effect = IT_VOLUME_SLIDE;
+						if (version == PSMV_OLD) entry->effectvalue = ((length&0x1e)<<3) | 0xF;
+						else if (version == PSMV_NEW) entry->effectvalue = (length<<4) | 0xF;
+						break;
+
+					case 2:
+						entry->effect = IT_VOLUME_SLIDE;
+						if (version == PSMV_OLD) entry->effectvalue = (length << 3) & 0xF0;
+						else if (version == PSMV_NEW) entry->effectvalue = (length << 4) & 0xF0;
+						break;
+
+					case 3:
+						entry->effect = IT_VOLUME_SLIDE;
+						if (version == PSMV_OLD) entry->effectvalue = (length >> 1) | 0xF0;
+						else if (version == PSMV_NEW) entry->effectvalue = length | 0xF0;
+						break;
+
+					case 4:
+						entry->effect = IT_VOLUME_SLIDE;
+						if (version == PSMV_OLD) entry->effectvalue = (length >> 1) & 0xF;
+						else if (version == PSMV_NEW) entry->effectvalue = length & 0xF;
+						break;
+
+					case 12:
+						entry->effect = IT_PORTAMENTO_UP;
+						if (version == PSMV_OLD) {
+							if (length < 4) entry->effectvalue = length | 0xF0;
+							else entry->effectvalue = length >> 2;
+						} else if (version == PSMV_NEW) {
+							entry->effectvalue = length;
+						}
+						break;
+
+					case 14:
+						entry->effect = IT_PORTAMENTO_DOWN;
+						if (version == PSMV_OLD) {
+							if (length < 4) entry->effectvalue = length | 0xF0;
+							else entry->effectvalue = length >> 2;
+						} else if (version == PSMV_NEW) {
+							entry->effectvalue = length;
+						}
+						break;
+
+					case 15:
+						entry->effect = IT_TONE_PORTAMENTO;
+						if (version == PSMV_OLD) entry->effectvalue = length >> 2;
+						else if (version == PSMV_NEW) entry->effectvalue = length;
+						break;
+
+					case 0x15:
+						entry->effect = IT_VIBRATO;
+						entry->effectvalue = length;
+						break;
+
+					case 0x18:
+						entry->effect = IT_VOLSLIDE_VIBRATO;
+						entry->effectvalue = length;
+						break;
+
+					case 0x29:
+						entry->effect = IT_SET_SAMPLE_OFFSET;
+						entry->effectvalue = data[pos+2];
+						pos += 2;
+						break;
+
+					case 0x2A:
+						entry->effect = IT_RETRIGGER_NOTE;
+						entry->effectvalue = length;
+						break;
+
+					case 0x33:
+#if 0
+						entry->effect = IT_POSITION_JUMP;
+						entry->effectvalue = data[pos+2];
+#else
+						entry->mask &= ~IT_ENTRY_EFFECT;
+#endif
+						pos++;
+						break;
+
+					case 0x34:
+						entry->effect = IT_BREAK_TO_ROW;
+						entry->effectvalue = length;
+						break;
+
+					case 0x3D:
+						entry->effect = IT_SET_SPEED;
+						entry->effectvalue = length;
+						break;
+
+					case 0x3E:
+						if (length >= 0x20) {
+							entry->effect = IT_SET_SONG_TEMPO;
+							entry->effectvalue = length;
+						} else {
+							entry->mask &= ~IT_ENTRY_EFFECT;
+						}
+						break;
+
+					case 0x47:
+						entry->effect = IT_ARPEGGIO;
+						entry->effectvalue = length;
+						break;
+
+					default:
+						return -1;
+				}
+				pos += 2;
+			}
+			if (entry->mask) entry++;
+		}
+	}
+
+	while (row < nrows) {
+		IT_SET_END_ROW(entry);
+		entry++;
+		row++;
+	}
+
+	pattern->n_entries = entry - pattern->entry;
+	if (!pattern->n_entries) return -1;
+
+	return 0;
+}
+
+
+static void free_chunks(PSMCHUNK * chunk, int count) {
+	int n;
+
+	for (n = 0; n < count; n++) {
+		if (chunk[n].data)
+			free(chunk[n].data);
+	}
+
+	free(chunk);
+}
+
+static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata);
+
+static DUMB_IT_SIGDATA *it_psm_load_sigdata(DUMBFILE *f, int * ver, int subsong)
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	PSMCHUNK *chunk;
+	int n_chunks = 0;
+
+	PSMCHUNK *songchunk;
+	int n_song_chunks = 0;
+
+	PSMEVENT *event;
+	int n_events = 0;
+
+	unsigned char * ptr;
+
+	int n, length, o;
+
+	int found;
+
+	int n_patterns = 0;
+
+	int first_pattern_line = -1;
+	int first_pattern;
+
+	int speed, bpm;
+	unsigned char pan[64];
+	int vol[32];
+
+	if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) goto error;
+
+	length = dumbfile_igetl(f);
+
+	if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) goto error;
+
+	chunk = malloc(768 * sizeof(*chunk));
+
+	while (length >= 8) {
+		chunk[n_chunks].id = dumbfile_mgetl(f);
+		n = dumbfile_igetl(f);
+		length -= 8;
+		if (n < 0 || n > length)
+			goto error_fc;
+		chunk[n_chunks].len = n;
+		if (n) {
+			ptr = malloc(n);
+			if (!ptr) goto error_fc;
+			if (dumbfile_getnc(ptr, n, f) < n) goto error_fc;
+			chunk[n_chunks].data = ptr;
+		}
+		n_chunks++;
+		length -= n;
+	}
+
+	if (!n_chunks) goto error_fc;
+				
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) goto error_fc;
+
+	sigdata->n_patterns = 0;
+	sigdata->n_samples = 0;
+	sigdata->name[0] = 0;
+
+	found = 0;
+
+	for (n = 0; n < n_chunks; n++) {
+		PSMCHUNK * c = &chunk[n];
+		switch(c->id) {
+		case DUMB_ID('S','D','F','T'):
+			/* song data format? */
+			if ((found & 1) || (c->len != 8) || memcmp(c->data, "MAINSONG", 8)) goto error_sd;
+			found |= 1;
+			break;
+
+		case DUMB_ID('S','O','N','G'):
+			if (/*(found & 2) ||*/ (c->len < 11) /*|| memcmp(c->data, "MAINSONG", 8)*/) goto error_sd;
+			found |= 2;
+			break;
+
+		case DUMB_ID('D','S','M','P'):
+			sigdata->n_samples++;
+			break;
+
+		case DUMB_ID('T','I','T','L'):
+			length = min(sizeof(sigdata->name) - 1, c->len);
+			memcpy(sigdata->name, c->data, length);
+			sigdata->name[length] = 0;
+		}
+	}
+
+	if (found != 3 || !sigdata->n_samples) goto error_sd;
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_instruments = 0;
+	sigdata->n_orders = 0;
+
+	for (n = 0; n < n_chunks; n++) {
+		PSMCHUNK * c = &chunk[n];
+		if (c->id == DUMB_ID('S','O','N','G')) {
+			if (subsong == 0) break;
+			subsong--;
+		}
+	}
+
+	if (n == n_chunks) return NULL;
+	subsong = n;
+
+	/*for (n = 0; n < n_chunks; n++) {
+		PSMCHUNK * c = &chunk[n];
+		if (c->id == DUMB_ID('S','O','N','G')) {*/
+	{
+		PSMCHUNK * c = &chunk[subsong];
+		{
+			ptr = c->data;
+			if (ptr[10] > 32) goto error_usd;
+			sigdata->n_pchannels = ptr[10];
+			length = c->len - 11;
+			ptr += 11;
+			if (length >= 8) {
+				songchunk = malloc(128 * sizeof(*songchunk));
+				if (!songchunk) goto error_usd;
+				while (length >= 8) {
+					songchunk[n_song_chunks].id = DUMB_ID(ptr[0], ptr[1], ptr[2], ptr[3]);
+					n = ptr[4] | (ptr[5] << 8) | (ptr[6] << 16) | (ptr[7] << 24);
+					length -= 8;
+					if (n > length) goto error_sc;
+					songchunk[n_song_chunks].len = n;
+					songchunk[n_song_chunks].data = ptr + 8;
+					n_song_chunks++;
+					length -= n;
+					ptr += 8 + n;
+				}
+			}
+			/*break;*/
+		}
+	}
+
+	if (!n_song_chunks) goto error_usd;
+
+	for (n = 0; n < n_song_chunks; n++) {
+		PSMCHUNK * c = &songchunk[n];
+
+		if (c->id == DUMB_ID('D','A','T','E')) {
+			/* date of the library build / format spec */
+			found = 0;
+			if (c->len == 6) {
+				found = 0;
+				length = c->len;
+				ptr = c->data;
+				while (length > 0) {
+					if (*ptr >= '0' && *ptr <= '9') {
+						found = (found * 10) + (*ptr - '0');
+					} else {
+						found = 0;
+						break;
+					}
+					ptr++;
+					length--;
+				}
+			}
+			break;
+		}
+	}
+
+	/*
+	if (found != 940506 &&
+		found != 940509 &&
+		found != 940510 &&
+		found != 940530 &&
+		found != 940629 &&
+		found != PSMV_OLD &&
+		found != 941011 &&
+		found != PSMV_NEW &&
+		found != 940906 &&
+		found != 940903 &&
+		found != 940914 &&
+		found != 941213 &&
+		found != 800211)   /* WTF?
+		goto error_sc;
+	*/
+
+	*ver = found;
+
+	if (found == 800211 ||
+		found == PSMV_NEW ||
+		found == 940903 ||
+		found == 940906 ||
+		found == 940914 ||
+		found == 941213) found = PSMV_NEW;
+	else found = PSMV_OLD;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+		sigdata->channel_pan[n  ] = 16;
+		sigdata->channel_pan[n+1] = 48;
+		sigdata->channel_pan[n+2] = 48;
+		sigdata->channel_pan[n+3] = 16;
+	}
+
+	for (n = 0; n < n_song_chunks; n++) {
+		PSMCHUNK * c = &songchunk[n];
+
+		switch (c->id) {
+			case DUMB_ID('O','P','L','H'):
+				if (c->len < 2) goto error_sc;
+				ptr = c->data;
+				o = ptr[0] | (ptr[1] << 8);
+				if (!o) goto error_sc;
+				event = malloc(o * sizeof(*event));
+				if (!event) goto error_sc;
+				length = c->len - 2;
+				ptr += 2;
+				while ((length > 0) && (n_events < o)) {
+					event[n_events].type = *ptr;
+					switch (*ptr) {
+					case PSM_EVENT_END:
+						ptr++;
+						length--;
+						break;
+
+					case PSM_EVENT_PLAY_PATTERN:
+						if (found == PSMV_OLD) {
+							if (length < 5) goto error_ev;
+							memcpy(event[n_events].data, ptr + 1, 4);
+							ptr += 5;
+							length -= 5;
+						} else if (found == PSMV_NEW) {
+							if (length < 9) goto error_ev;
+							memcpy(event[n_events].data, ptr + 1, 8);
+							ptr += 9;
+							length -= 9;
+						}
+						break;
+
+					case PSM_EVENT_SET_SPEED:
+					case PSM_EVENT_SET_BPM:
+						if (length < 2) goto error_ev;
+						event[n_events].data[0] = ptr[1];
+						ptr += 2;
+						length -= 2;
+						break;
+
+					case PSM_EVENT_JUMP_TO_LINE:
+					case PSM_EVENT_CHANGE_VOL:
+						if (length < 3) goto error_ev;
+						memcpy(event[n_events].data, ptr + 1, 2);
+						ptr += 3;
+						length -= 3;
+						break;
+
+					case PSM_EVENT_SAMPLE_MAP_TABLE:
+						if (length < 7) goto error_ev;
+						memcpy(event[n_events].data, ptr + 1, 6);
+						ptr += 7;
+						length -= 7;
+						break;
+
+					case PSM_EVENT_CHANGE_PAN:
+						if (length < 4) goto error_ev;
+						memcpy(event[n_events].data, ptr + 1, 3);
+						ptr += 4;
+						length -= 4;
+						break;
+
+					default:
+						goto error_ev;
+					}
+					n_events++;
+				}
+				break;
+
+			case DUMB_ID('P','P','A','N'):
+				length = c->len;
+				if (length & 1) goto error_ev;
+				ptr = c->data;
+				o = 0;
+				while (length > 0) {
+					switch (ptr[0]) {
+					case 0:
+						sigdata->channel_pan[o] = ((((int)(signed char)ptr[1]) * 32) / 127) + 32;
+						break;
+					case 2:
+						sigdata->channel_pan[o] = IT_SURROUND;
+						break;
+					case 4:
+						sigdata->channel_pan[o] = 32;
+						break;
+					}
+					ptr += 2;
+					length -= 2;
+					if (++o >= DUMB_IT_N_CHANNELS) break;
+				}
+				break;
+
+			/*
+			case DUMB_ID('P','A','T','T'):
+			case DUMB_ID('D','S','A','M'):
+			*/
+		}
+	}
+
+	sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
+
+	sigdata->global_volume = 128;
+	sigdata->speed = 6;
+	sigdata->tempo = 125;
+	sigdata->mixing_volume = 48;
+	sigdata->pan_separation = 128;
+
+	speed = 0;
+	bpm = 0;
+	memset(pan, 255, sizeof(pan));
+	memset(vol, 255, sizeof(vol));
+
+	sigdata->n_patterns = n_events;
+	sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+	if (!sigdata->pattern) goto error_ev;
+	for (n = 0; n < sigdata->n_patterns; n++)
+		sigdata->pattern[n].entry = NULL;
+
+	for (n = 0; n < n_events; n++) {
+		PSMEVENT * e = &event[n];
+		switch (e->type) {
+		case PSM_EVENT_END:
+			n = n_events;
+			break;
+
+		case PSM_EVENT_PLAY_PATTERN:
+			for (o = 0; o < n_chunks; o++) {
+				PSMCHUNK * c = &chunk[o];
+				if (c->id == DUMB_ID('P','B','O','D')) {
+					ptr = c->data;
+					length = c->len;
+					if (found == PSMV_OLD) {
+						if (length < 8) goto error_ev;
+						if (!memcmp(ptr + 4, e->data, 4)) {
+							if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev;
+							if (first_pattern_line < 0) {
+								first_pattern_line = n;
+								first_pattern = o;
+							}
+							e->data[0] = n_patterns;
+							e->data[1] = n_patterns >> 8;
+							n_patterns++;
+							break;
+						}
+					} else if (found == PSMV_NEW) {
+						if (length < 12) goto error_ev;
+						if (!memcmp(ptr + 4, e->data, 8)) {
+							if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev;
+							if (first_pattern_line < 0) {
+								first_pattern_line = n;
+								first_pattern = o;
+							}
+							e->data[0] = n_patterns;
+							e->data[1] = n_patterns >> 8;
+							n_patterns++;
+							break;
+						}
+					}
+				}
+			}
+			if (o == n_chunks) goto error_ev;
+
+			speed = 0;
+			bpm = 0;
+			memset(pan, 255, sizeof(pan));
+			memset(vol, 255, sizeof(vol));
+
+			e->type = PSM_EVENT_END;
+			break;
+
+		case PSM_EVENT_JUMP_TO_LINE:
+			o = e->data[0] | (e->data[1] << 8);
+			if (o >= n_events) goto error_ev;
+			if (o == 0) {
+				/* whew! easy case! */
+				sigdata->restart_position = 0;
+				n = n_events;
+			} else if (o == n) {
+				/* freeze */
+				n = n_events;
+			} else if (o > n) {
+				/* jump ahead, setting played event numbers to zero will prevent endless looping */
+				n = o - 1;
+			} else if (o >= first_pattern_line) {
+				/* another semi-easy case */
+				sigdata->restart_position = event[o].data[0] | (event[o].data[1] << 8);
+				n = n_events;
+			} else {
+				/* crud, try to simulate rerunning all of the commands from the indicated
+				 * line up to the first pattern, then dupe the first pattern again.
+				 */
+				/*
+				PSMCHUNK * c = &chunk[first_pattern];
+
+				for (; o < first_pattern_line; o++) {
+					PSMEVENT * ev = &event[o];
+					switch (ev->type) {
+					case PSM_EVENT_SET_SPEED:
+						speed = ev->data[0];
+						break;
+					case PSM_EVENT_SET_BPM:
+						bpm = ev->data[0];
+						break;
+					case PSM_EVENT_CHANGE_PAN:
+						if (ev->data[0] > 31) goto error_ev;
+						pan[ev->data[0] * 2] = ev->data[1];
+						pan[ev->data[0] * 2 + 1] = ev->data[2];
+						break;
+					case PSM_EVENT_CHANGE_VOL:
+						if (ev->data[0] > 31) goto error_ev;
+						vol[ev->data[0]] = ev->data[1];
+						break;
+					}
+				}
+
+				if (it_psm_process_pattern(&sigdata->pattern[n_patterns], c->data, c->len, speed, bpm, pan, vol, found)) goto error_ev;
+				n_patterns++;
+				sigdata->restart_position = 1;
+				n = n_events;
+
+				Eh, what the hell? PSM has no panning commands anyway.
+				*/
+				sigdata->restart_position = 0;
+				n = n_events;
+			}
+			e->type = PSM_EVENT_END;
+			break;
+
+		case PSM_EVENT_SET_SPEED:
+			speed = e->data[0];
+			break;
+
+		case PSM_EVENT_SET_BPM:
+			bpm = e->data[0];
+			break;
+
+		case PSM_EVENT_CHANGE_PAN:
+			o = e->data[0];
+			if (o > 31) goto error_ev;
+			pan[o * 2] = e->data[1];
+			pan[o * 2 + 1] = e->data[2];
+			break;
+
+		case PSM_EVENT_CHANGE_VOL:
+			o = e->data[0];
+			if (o > 31) goto error_ev;
+			vol[o] = e->data[1];
+			break;
+
+		case PSM_EVENT_SAMPLE_MAP_TABLE:
+			if (e->data[0] != 0 || e->data[1] != 0xFF ||
+				e->data[2] != 0 || e->data[3] != 0 ||
+				e->data[4] != 1 || e->data[5] != 0)
+				goto error_ev;
+			break;
+		}
+	}
+
+	if (n_patterns > 256) goto error_ev;
+
+	sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+	if (!sigdata->sample) goto error_ev;
+	for (n = 0; n < sigdata->n_samples; n++)
+		sigdata->sample[n].right = sigdata->sample[n].left = NULL;
+
+	o = 0;
+	for (n = 0; n < n_chunks; n++) {
+		PSMCHUNK * c = &chunk[n];
+		if (c->id == DUMB_ID('D','S','M','P')) {
+			if (it_psm_process_sample(&sigdata->sample[o], c->data, c->len, o, found)) goto error_ev;
+			o++;
+		}
+	}
+
+	sigdata->n_orders = n_patterns;
+	sigdata->n_patterns = n_patterns;
+
+	sigdata->order = malloc(n_patterns);
+
+	for (n = 0; n < n_patterns; n++) {
+		sigdata->order[n] = n;
+	}
+
+	free(event);
+	free(songchunk);
+	free_chunks(chunk, n_chunks);
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	dumb_it_optimize_orders(sigdata);
+
+	return sigdata;
+
+error_ev:
+	free(event);
+error_sc:
+	free(songchunk);
+error_usd:
+	_dumb_it_unload_sigdata(sigdata);
+	goto error_fc;
+error_sd:
+	free(sigdata);
+error_fc:
+	free_chunks(chunk, n_chunks);
+error:
+	return NULL;
+}
+
+static int it_order_compare(const void *e1, const void *e2) {
+	if (*((const char *)e1) < *((const char *)e2))
+		return -1;
+
+	if (*((const char *)e1) > *((const char *)e2))
+		return 1;
+
+	return 0;
+}
+
+static int it_optimize_compare(const void *e1, const void *e2) {
+	if (((const IT_ENTRY *)e1)->channel < ((const IT_ENTRY *)e2)->channel)
+		return -1;
+
+	if (((const IT_ENTRY *)e1)->channel > ((const IT_ENTRY *)e2)->channel)
+		return 1;
+
+	return 0;
+}
+
+static int it_entry_compare(const IT_ENTRY * e1, const IT_ENTRY * e2) {
+	if (IT_IS_END_ROW(e1) && IT_IS_END_ROW(e2)) return 1;
+	if (e1->channel != e2->channel) return 0;
+	if (e1->mask != e2->mask) return 0;
+	if ((e1->mask & IT_ENTRY_NOTE) && (e1->note != e2->note)) return 0;
+	if ((e1->mask & IT_ENTRY_INSTRUMENT) && (e1->instrument != e2->instrument)) return 0;
+	if ((e1->mask & IT_ENTRY_VOLPAN) && (e1->volpan != e2->volpan)) return 0;
+	if ((e1->mask & IT_ENTRY_EFFECT) && ((e1->effect != e2->effect) || (e1->effectvalue != e2->effectvalue))) return 0;
+	return 1;
+}
+
+/*
+static void dumb_it_optimize_pattern(IT_PATTERN * pattern) {
+	IT_ENTRY * entry, * end;
+	IT_ENTRY * rowstart, * rowend;
+	IT_ENTRY * current;
+
+	if (!pattern->n_entries || !pattern->entry) return;
+
+	current = entry = pattern->entry;
+	end = entry + pattern->n_entries;
+
+	while (entry < end) {
+		rowstart = entry;
+		while (!IT_IS_END_ROW(entry)) entry++;
+		rowend = entry;
+		if (rowend > rowstart + 1)
+			qsort(rowstart, rowend - rowstart, sizeof(IT_ENTRY), &it_optimize_compare);
+		entry = rowstart;
+		while (entry < rowend) {
+			if (!(entry->mask)) {}
+			else if (it_entry_compare(entry, current)) {}
+			else if (!(current->mask) ||
+					 ((entry->channel == current->channel) &&
+					 ((entry->mask | current->mask) == (entry->mask ^ current->mask)))) {
+				current->mask |= entry->mask;
+				if (entry->mask & IT_ENTRY_NOTE) current->note = entry->note;
+				if (entry->mask & IT_ENTRY_INSTRUMENT) current->instrument = entry->instrument;
+				if (entry->mask & IT_ENTRY_VOLPAN) current->volpan = entry->volpan;
+				if (entry->mask & IT_ENTRY_EFFECT) {
+					current->effect = entry->effect;
+					current->effectvalue = entry->effectvalue;
+				}
+			} else {
+				if (++current < entry) *current = *entry;
+			}
+			entry++;
+		}
+		if (++current < entry) *current = *entry;
+		entry++;
+	}
+
+	current++;
+
+	if (current < end) {
+		IT_ENTRY * opt;
+		pattern->n_entries = current - pattern->entry;
+		opt = realloc(pattern->entry, pattern->n_entries * sizeof(*pattern->entry));
+		if (opt) pattern->entry = opt;
+	}
+}
+*/
+
+static int it_pattern_compare(const IT_PATTERN * p1, const IT_PATTERN * p2) {
+	IT_ENTRY * e1, * end;
+	IT_ENTRY * e2;
+
+	if (p1 == p2) return 1;
+	if (p1->n_entries != p2->n_entries) return 0;
+	
+	e1 = p1->entry; end = e1 + p1->n_entries;
+	e2 = p2->entry;
+
+	while (e1 < end) {
+		if (!it_entry_compare(e1, e2)) return 0;
+		e1++; e2++;
+	}
+
+	return 1;
+}
+
+static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata) {
+	int n, o, p;
+
+	int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;
+
+	unsigned char * order_list;
+	int n_patterns;
+
+	IT_PATTERN * pattern;
+
+	if (!sigdata->n_orders || !sigdata->n_patterns) return;
+
+	n_patterns = 0;
+	order_list = malloc(sigdata->n_orders);
+
+	if (!order_list) return;
+
+	for (n = 0; n < sigdata->n_orders; n++) {
+		if (sigdata->order[n] < sigdata->n_patterns) {
+			for (o = 0; o < n_patterns; o++) {
+				if (sigdata->order[n] == order_list[o]) break;
+			}
+			if (o == n_patterns) {
+				order_list[n_patterns++] = sigdata->order[n];
+			}
+		}
+	}
+
+	if (!n_patterns) {
+		free(order_list);
+		return;
+	}
+
+	/*for (n = 0; n < n_patterns; n++) {
+		dumb_it_optimize_pattern(&sigdata->pattern[order_list[n]]);
+	}*/
+
+	for (n = 0; n < n_patterns; n++) {
+		for (o = n + 1; o < n_patterns; o++) {
+			if ((order_list[n] != order_list[o]) &&
+				it_pattern_compare(&sigdata->pattern[order_list[n]], &sigdata->pattern[order_list[o]])) {
+				for (p = 0; p < sigdata->n_orders; p++) {
+					if (sigdata->order[p] == order_list[o]) {
+						sigdata->order[p] = order_list[n];
+					}
+				}
+				for (p = o + 1; p < n_patterns; p++) {
+					if (order_list[p] == order_list[o]) {
+						order_list[p] = order_list[n];
+					}
+				}
+				order_list[o] = order_list[n];
+			}
+		}
+	}
+
+	qsort(order_list, n_patterns, sizeof(*order_list), &it_order_compare);
+
+	for (n = 0, o = 0; n < n_patterns; n++) {
+		if (order_list[n] != order_list[o]) {
+			if (++o < n) order_list[o] = order_list[n];
+		}
+	}
+
+	n_patterns = o + 1;
+
+	pattern = malloc(n_patterns * sizeof(*pattern));
+	if (!pattern) {
+		free(order_list);
+		return;
+	}
+
+	for (n = 0; n < n_patterns; n++) {
+		pattern[n] = sigdata->pattern[order_list[n]];
+	}
+
+	for (n = 0; n < sigdata->n_patterns; n++) {
+		for (o = 0; o < n_patterns; o++) {
+			if (order_list[o] == n) break;
+		}
+		if (o == n_patterns) {
+			if (sigdata->pattern[n].entry)
+				free(sigdata->pattern[n].entry);
+		}
+	}
+
+	free(sigdata->pattern);
+	sigdata->pattern = pattern;
+	sigdata->n_patterns = n_patterns;
+
+	for (n = 0; n < sigdata->n_orders; n++) {
+		for (o = 0; o < n_patterns; o++) {
+			if (sigdata->order[n] == order_list[o]) {
+				sigdata->order[n] = o;
+				break;
+			}
+		}
+	}
+
+	free(order_list);
+}
+
+int dumb_get_psm_subsong_count(DUMBFILE *f) {
+	int length, subsongs;
+	long l;
+	
+	if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) return 0;
+
+	length = dumbfile_igetl(f);
+
+	if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) return 0;
+
+	subsongs = 0;
+
+	while (length >= 8 && !dumbfile_error(f)) {
+		if (dumbfile_mgetl(f) == DUMB_ID('S','O','N','G')) subsongs++;
+		l = dumbfile_igetl(f);
+		dumbfile_skip(f, l);
+		length -= l + 8;
+	}
+
+	if (dumbfile_error(f)) return 0;
+
+	return subsongs;
+}
+
+DUH *dumb_read_psm(DUMBFILE *f, int subsong)
+{
+	sigdata_t *sigdata;
+	long length;
+	int ver;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_psm_load_sigdata(f, &ver, subsong);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		char version[16];
+		const char *tag[3][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "PSM";
+		tag[2][0] = "FORMATVERSION";
+		itoa(ver, version, 10);
+		tag[2][1] = (const char *) &version;
+		return make_duh(length, 3, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readptm.c
@@ -1,0 +1,576 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readptm.c - Code to read a Poly Tracker v2.03      / / \  \
+ *             module from an open file.             | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller. Based on reads3m.c               \_  /  > /
+ * by entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+// IT_STEREO... :o
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/** WARNING: this is duplicated in itread.c */
+static int it_seek(DUMBFILE *f, long offset)
+{
+	long pos = dumbfile_pos(f);
+
+	if (pos > offset)
+		return -1;
+
+	if (pos < offset)
+		if (dumbfile_skip(f, offset - pos))
+			return -1;
+
+	return 0;
+}
+
+
+
+static int it_ptm_read_sample_header(IT_SAMPLE *sample, long *offset, DUMBFILE *f)
+{
+	int flags;
+
+	flags = dumbfile_getc(f);
+
+	dumbfile_getnc(sample->filename, 12, f);
+	sample->filename[12] = 0;
+
+	sample->default_volume = dumbfile_getc(f);
+
+	sample->C5_speed = dumbfile_igetw(f) << 1;
+
+	dumbfile_skip(f, 2); /* segment */
+
+	*offset = dumbfile_igetl(f);
+
+	sample->length = dumbfile_igetl(f);
+	sample->loop_start = dumbfile_igetl(f);
+	sample->loop_end = dumbfile_igetl(f);
+
+	/* GUSBegin, GUSLStart, GUSLEnd, GUSLoop, reserverd */
+	dumbfile_skip(f, 4+4+4+1+1);
+
+	dumbfile_getnc(sample->name, 28, f);
+	sample->name[28] = 0;
+
+	/*
+	if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','S'))
+		return -1;
+	*/
+
+	/* BLAH! Shit likes to have broken or missing sample IDs */
+	dumbfile_skip(f, 4);
+
+	if ((flags & 3) == 0) {
+		/* Looks like no sample */
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+		return dumbfile_error(f);
+	}
+
+	sample->global_volume = 64;
+
+	sample->flags = IT_SAMPLE_EXISTS;
+	if (flags & 4) sample->flags |= IT_SAMPLE_LOOP;
+	if (flags & 8) sample->flags |= IT_SAMPLE_PINGPONG_LOOP;
+
+	if (flags & 16) {
+		sample->flags |= IT_SAMPLE_16BIT;
+
+		sample->length >>= 1;
+		sample->loop_start >>= 1;
+		sample->loop_end >>= 1;
+	}
+
+	if (sample->loop_end) sample->loop_end--;
+
+	sample->default_pan = 0; // 0 = don't use, or 160 = centre?
+
+	if (sample->length <= 0)
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+	else if (sample->flags & IT_SAMPLE_LOOP) {
+		if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+			sample->flags &= ~IT_SAMPLE_LOOP;
+		else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+			sample->flags &= ~IT_SAMPLE_LOOP;
+		else
+			sample->length = sample->loop_end;
+	}
+
+
+	//Do we need to set all these?
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = IT_VIBRATO_SINE;
+	sample->max_resampling_quality = -1;
+
+	return dumbfile_error(f);
+}
+
+
+static int it_ptm_read_byte(DUMBFILE *f)
+{
+	int meh = dumbfile_getc(f);
+	if (meh < 0) return 0;
+	return meh;
+}
+
+static int it_ptm_read_sample_data(IT_SAMPLE *sample, int last, DUMBFILE *f)
+{
+	long n;
+	int s;
+
+	sample->left = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+	if (!sample->left)
+		return -1;
+
+	s = 0;
+
+	if (sample->flags & IT_SAMPLE_16BIT) {
+		unsigned char a, b;
+		for (n = 0; n < sample->length; n++) {
+			a = s += (signed char) it_ptm_read_byte(f);
+			b = s += (signed char) it_ptm_read_byte(f);
+			((short *)sample->left)[n] = a | (b << 8);
+		}
+	} else {
+		for (n = 0; n < sample->length; n++) {
+			s += (signed char) it_ptm_read_byte(f);
+			((signed char *)sample->left)[n] = s;
+		}
+	}
+
+	if (dumbfile_error(f) && !last)
+		return -1;
+
+	return 0;
+}
+
+
+
+static int it_ptm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int length)
+{
+	int buflen = 0;
+	int bufpos = 0;
+	int effect, effectvalue;
+
+	IT_ENTRY *entry;
+
+	unsigned char channel;
+
+	if (!length)
+		return -1;
+
+	pattern->n_rows = 0;
+	pattern->n_entries = 0;
+
+	/* Read in the pattern data, little by little, and work out how many
+	 * entries we need room for. Sorry, but this is just so funny...
+	 */
+	for (;;) {
+		unsigned char b = buffer[buflen++] = dumbfile_getc(f);
+
+#if 1
+		static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5};
+		channel = b & 31;
+		b >>= 5;
+		pattern->n_entries++;
+		if (b) {
+			if (buflen + used[b] >= 65536) return -1;
+			dumbfile_getnc(buffer + buflen, used[b], f);
+			buflen += used[b];
+		} else {
+			/* End of row */
+			if (++pattern->n_rows == 64) break;
+			if (buflen >= 65536) return -1;
+		}
+#else
+		if (b == 0) {
+			/* End of row */
+			pattern->n_entries++;
+			if (++pattern->n_rows == 64) break;
+			if (buflen >= 65536) return -1;
+		} else {
+			static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5};
+			channel = b & 31;
+			b >>= 5;
+			if (b) {
+				pattern->n_entries++;
+				if (buflen + used[b] >= 65536) return -1;
+				dumbfile_getnc(buffer + buflen, used[b], f);
+				buflen += used[b];
+			}
+		}
+#endif
+
+		/* We have ensured that buflen < 65536 at this point, so it is safe
+		 * to iterate and read at least one more byte without checking.
+		 * However, now would be a good time to check for errors reading from
+		 * the file.
+		 */
+
+		if (dumbfile_error(f))
+			return -1;
+
+		/* Great. We ran out of data, but there should be data for more rows.
+		 * Fill the rest with null data...
+		 */
+		if (buflen >= length && pattern->n_rows < 64)
+		{
+			while (pattern->n_rows < 64)
+			{
+				if (buflen >= 65536) return -1;
+				buffer[buflen++] = 0;
+				pattern->n_entries++;
+				pattern->n_rows++;
+			}
+			break;
+		}
+	}
+
+	pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+
+	if (!pattern->entry)
+		return -1;
+
+	entry = pattern->entry;
+
+	while (bufpos < buflen) {
+		unsigned char b = buffer[bufpos++];
+
+		if (b == 0)
+		{
+			/* End of row */
+			IT_SET_END_ROW(entry);
+			entry++;
+			continue;
+		}
+
+		channel = b & 31;
+
+		if (b & 224) {
+			entry->mask = 0;
+			entry->channel = channel;
+
+			if (b & 32) {
+				unsigned char n = buffer[bufpos++];
+				if (n == 254 || (n >= 1 && n <= 120)) {
+					if (n == 254)
+						entry->note = IT_NOTE_CUT;
+					else
+						entry->note = n - 1;
+					entry->mask |= IT_ENTRY_NOTE;
+				}
+
+				entry->instrument = buffer[bufpos++];
+				if (entry->instrument)
+					entry->mask |= IT_ENTRY_INSTRUMENT;
+			}
+
+			if (b & 64) {
+				effect = buffer[bufpos++];
+				effectvalue = buffer[bufpos++];
+				_dumb_it_ptm_convert_effect(effect, effectvalue, entry);
+			}
+
+			if (b & 128) {
+				entry->volpan = buffer[bufpos++];
+				if (entry->volpan <= 64)
+					entry->mask |= IT_ENTRY_VOLPAN;
+			}
+
+			entry++;
+		}
+	}
+
+	ASSERT(entry == pattern->entry + pattern->n_entries);
+
+	return 0;
+}
+
+
+
+/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */
+/* Currently we assume the sample data are stored after the sample headers in
+ * module files. This assumption may be unjustified; let me know if you have
+ * trouble.
+ */
+
+#define IT_COMPONENT_INSTRUMENT 1
+#define IT_COMPONENT_PATTERN    2
+#define IT_COMPONENT_SAMPLE     3
+
+typedef struct IT_COMPONENT
+{
+	unsigned char type;
+	unsigned char n;
+	long offset;
+}
+IT_COMPONENT;
+
+
+
+static int it_component_compare(const void *e1, const void *e2)
+{
+	return ((const IT_COMPONENT *)e1)->offset -
+	       ((const IT_COMPONENT *)e2)->offset;
+}
+
+
+
+static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f)
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	IT_COMPONENT *component;
+	int n_components = 0;
+
+	int n;
+
+	unsigned char *buffer;
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) return NULL;
+
+	/* Skip song name. */
+	dumbfile_getnc(sigdata->name, 28, f);
+	sigdata->name[28] = 0;
+
+	if (dumbfile_getc(f) != 0x1A || dumbfile_igetw(f) != 0x203) {
+		free(sigdata);
+		return NULL;
+	}
+
+	dumbfile_skip(f, 1);
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_orders = dumbfile_igetw(f);
+	sigdata->n_instruments = 0;
+	sigdata->n_samples = dumbfile_igetw(f);
+	sigdata->n_patterns = dumbfile_igetw(f);
+
+	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 255 || sigdata->n_patterns > 128) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	sigdata->n_pchannels = dumbfile_igetw(f);
+
+	if (dumbfile_igetw(f) != 0) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	dumbfile_skip(f, 2);
+
+	if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','F')) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	dumbfile_skip(f, 16);
+
+	sigdata->order = malloc(sigdata->n_orders);
+	if (!sigdata->order) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	if (sigdata->n_samples) {
+		sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+		if (!sigdata->sample) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (n = 0; n < sigdata->n_samples; n++)
+			sigdata->sample[n].right = sigdata->sample[n].left = NULL;
+	}
+
+	if (sigdata->n_patterns) {
+		sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+		if (!sigdata->pattern) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (n = 0; n < sigdata->n_patterns; n++)
+			sigdata->pattern[n].entry = NULL;
+	}
+
+	/** WARNING: which ones? */
+	sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_A_PTM;
+
+	sigdata->global_volume = 128;
+	sigdata->speed = 6;
+	sigdata->tempo = 125;
+	sigdata->mixing_volume = 48;
+
+	/* Panning positions for 32 channels */
+	{
+		int i;
+		for (i = 0; i < 32; i++) {
+			int c = dumbfile_getc(f);
+			if (c <= 15) {
+				sigdata->channel_volume[i] = 64;
+				sigdata->channel_pan[i] = c;
+			} else {
+				/** WARNING: this could be improved if we support channel muting... */
+				sigdata->channel_volume[i] = 0;
+				sigdata->channel_pan[i] = 7;
+			}
+		}
+	}
+
+	/* Orders, byte each, length = sigdata->n_orders (should be even) */
+	dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+	sigdata->restart_position = 0;
+
+	component = malloc(768*sizeof(*component));
+	if (!component) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	if (it_seek(f, 352)) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (n = 0; n < sigdata->n_patterns; n++) {
+		component[n_components].type = IT_COMPONENT_PATTERN;
+		component[n_components].n = n;
+		component[n_components].offset = dumbfile_igetw(f) << 4;
+		n_components++;
+	}
+
+	if (it_seek(f, 608)) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (n = 0; n < sigdata->n_samples; n++) {
+		if (it_ptm_read_sample_header(&sigdata->sample[n], &component[n_components].offset, f)) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		if (!(sigdata->sample[n].flags & IT_SAMPLE_EXISTS)) continue;
+		component[n_components].type = IT_COMPONENT_SAMPLE;
+		component[n_components].n = n;
+		n_components++;
+	}
+
+	qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
+
+	{
+		int i;
+		for (i = 0; i < 32; i++) {
+			sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3;
+			sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7;
+			if (sigdata->channel_pan[i] > 64) sigdata->channel_pan[i] = 64;
+		}
+	}
+
+	sigdata->pan_separation = 128;
+
+	if (dumbfile_error(f)) {
+		free(component);
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	buffer = malloc(65536);
+	if (!buffer) {
+		free(component);
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (n = 0; n < n_components; n++) {
+		if (it_seek(f, component[n].offset)) {
+			free(buffer);
+			free(component);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+
+		switch (component[n].type) {
+
+			case IT_COMPONENT_PATTERN:
+				if (it_ptm_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+				break;
+
+			case IT_COMPONENT_SAMPLE:
+				if (it_ptm_read_sample_data(&sigdata->sample[component[n].n], (n + 1 == n_components), f)) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+		}
+	}
+
+	free(buffer);
+	free(component);
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	return sigdata;
+}
+
+static char hexdigit(int in)
+{
+	if (in < 10) return in + '0';
+	else return in + 'A' - 10;
+}
+
+DUH *dumb_read_ptm(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_ptm_load_sigdata(f);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "PTM";
+		return make_duh(length, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/reads3m.c
@@ -1,0 +1,819 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * reads3m.c - Code to read a ScreamTracker 3         / / \  \
+ *             module from an open file.             | <  /   \_
+ *                                                   |  \/ /\   /
+ * By entheh.                                         \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+// IT_STEREO... :o
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+/** WARNING: this is duplicated in itread.c */
+static int it_seek(DUMBFILE *f, long offset)
+{
+	long pos = dumbfile_pos(f);
+
+	if (pos > offset) {
+		return -1;
+	}
+
+	if (pos < offset)
+		if (dumbfile_skip(f, offset - pos))
+			return -1;
+
+	return 0;
+}
+
+
+
+static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, unsigned char *pack, int cwtv, DUMBFILE *f)
+{
+	unsigned char type;
+	int flags;
+
+	type = dumbfile_getc(f);
+
+	if (type > 1) {
+		/** WARNING: no adlib support */
+	}
+
+	dumbfile_getnc(sample->filename, 13, f);
+	sample->filename[13] = 0;
+
+	*offset = dumbfile_igetw(f) << 4;
+
+	sample->length = dumbfile_igetl(f);
+	sample->loop_start = dumbfile_igetl(f);
+	sample->loop_end = dumbfile_igetl(f);
+
+	sample->default_volume = dumbfile_getc(f);
+
+	dumbfile_skip(f, 1);
+
+	*pack = dumbfile_getc(f);
+
+	if (*pack < 0 || (*pack != 0 && *pack != 4))
+		/* Sample is packed apparently (or error reading from file). We don't
+		 * know how to read packed samples.
+		 */
+		return -1;
+
+	flags = dumbfile_getc(f);
+
+	sample->C5_speed = dumbfile_igetl(f) << 1;
+
+	/* Skip four unused bytes and three internal variables. */
+	dumbfile_skip(f, 4+2+2+4);
+
+	dumbfile_getnc(sample->name, 28, f);
+	sample->name[28] = 0;
+
+	if (type == 0 || sample->length <= 0) {
+		/* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+		return dumbfile_error(f);
+	}
+
+	if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S'))
+		return -1;
+
+	sample->global_volume = 64;
+
+	sample->flags = IT_SAMPLE_EXISTS;
+	if (flags & 1) sample->flags |= IT_SAMPLE_LOOP;
+
+	/* The ST3 TECH.DOC is unclear on this, but IMAGO Orpheus is not. Piece of crap. */
+
+	if (flags & 2) {
+		sample->flags |= IT_SAMPLE_STEREO;
+
+		if ((cwtv & 0xF000) == 0x2000) {
+			sample->length >>= 1;
+			sample->loop_start >>= 1;
+			sample->loop_end >>= 1;
+		}
+	}
+
+	if (flags & 4) {
+		sample->flags |= IT_SAMPLE_16BIT;
+
+		if ((cwtv & 0xF000) == 0x2000) {
+			sample->length >>= 1;
+			sample->loop_start >>= 1;
+			sample->loop_end >>= 1;
+		}
+	}
+
+	sample->default_pan = 0; // 0 = don't use, or 160 = centre?
+
+	if (sample->flags & IT_SAMPLE_LOOP) {
+		if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+			/*sample->flags &= ~IT_SAMPLE_LOOP;*/
+			sample->loop_end = sample->length;
+		else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+			sample->flags &= ~IT_SAMPLE_LOOP;
+		else
+			/* ScreamTracker seems not to save what comes after the loop end
+			 * point, but rather to assume it is a duplicate of what comes at
+			 * the loop start point. I am not completely sure of this though.
+			 * It is easy to evade; simply truncate the sample.
+			 */
+			sample->length = sample->loop_end;
+	}
+
+
+	//Do we need to set all these?
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = IT_VIBRATO_SINE;
+	sample->max_resampling_quality = -1;
+
+	return dumbfile_error(f);
+}
+
+
+
+static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, unsigned char pack, DUMBFILE *f)
+{
+	long n;
+
+	sample->left = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+	if (!sample->left)
+		return -1;
+
+	if (sample->flags & IT_SAMPLE_STEREO) {
+		sample->right = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+		if (!sample->right)
+			return -1;
+	}
+
+	if (pack == 4) {
+		if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+			return -1;
+	}
+	else if (sample->flags & IT_SAMPLE_STEREO) {
+		if (sample->flags & IT_SAMPLE_16BIT) {
+			for (n = 0; n < sample->length; n++)
+				((short *)sample->left)[n] = dumbfile_igetw(f);
+			for (n = 0; n < sample->length; n++)
+				((short *)sample->right)[n] = dumbfile_igetw(f);
+		} else {
+			for (n = 0; n < sample->length; n++)
+				((signed char *)sample->left)[n] = dumbfile_getc(f);
+			for (n = 0; n < sample->length; n++)
+				((signed char *)sample->right)[n] = dumbfile_getc(f);
+		}
+	} else if (sample->flags & IT_SAMPLE_16BIT)
+		for (n = 0; n < sample->length; n++)
+			((short *)sample->left)[n] = dumbfile_igetw(f);
+	else
+		for (n = 0; n < sample->length; n++)
+			((signed char *)sample->left)[n] = dumbfile_getc(f);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	if (ffi != 1) {
+		/* Convert to signed. */
+		if (sample->flags & IT_SAMPLE_16BIT)
+			for (n = 0; n < sample->length; n++)
+				((short *)sample->left)[n] ^= 0x8000;
+		else
+			for (n = 0; n < sample->length; n++)
+				((signed char *)sample->left)[n] ^= 0x80;
+
+		if (sample->right) {
+			if (sample->flags & IT_SAMPLE_16BIT)
+				for (n = 0; n < sample->length; n++)
+					((short *)sample->right)[n] ^= 0x8000;
+			else
+				for (n = 0; n < sample->length; n++)
+					((signed char *)sample->right)[n] ^= 0x80;
+		}
+	}
+
+	return 0;
+}
+
+
+
+static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int maxlen)
+{
+	int length;
+	int buflen = 0;
+	int bufpos = 0;
+
+	IT_ENTRY *entry;
+
+	unsigned char channel;
+
+	/* Haha, this is hilarious!
+	 *
+	 * Well, after some experimentation, it seems that different S3M writers
+	 * define the format in different ways. The S3M docs say that the first
+	 * two bytes hold the "length of [the] packed pattern", and the packed
+	 * pattern data follow. Judging by the contents of ARMANI.S3M, packaged
+	 * with ScreamTracker itself, the measure of length _includes_ the two
+	 * bytes used to store the length; in other words, we should read
+	 * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug
+	 * Tracker, excludes these two bytes, so (length) more bytes must be
+	 * read.
+	 *
+	 * Call me crazy, but I just find it insanely funny that the format was
+	 * misunderstood in this way :D
+	 *
+	 * Now we can't just risk reading two extra bytes, because then we
+	 * overshoot, and DUMBFILEs don't support backward seeking (for a good
+	 * reason). Luckily, there is a way. We can read the data little by
+	 * little, and stop when we have 64 rows in memory. Provided we protect
+	 * against buffer overflow, this method should work with all sensibly
+	 * written S3M files. If you find one for which it does not work, please
+	 * let me know at [email protected] so I can look at it.
+	 */
+
+	/* Discard the length. */
+	/* read at most length bytes, in case of retarded crap */
+	length = dumbfile_igetw(f);
+	
+	if (maxlen)
+	{
+		maxlen -= 2;
+		if (length > maxlen) length = maxlen;
+	}
+
+	if (dumbfile_error(f) || !length)
+		return -1;
+
+	pattern->n_rows = 0;
+	pattern->n_entries = 0;
+
+	/* Read in the pattern data, little by little, and work out how many
+	 * entries we need room for. Sorry, but this is just so funny...
+	 */
+	for (;;) {
+		unsigned char b = buffer[buflen++] = dumbfile_getc(f);
+
+#if 1
+		static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
+		channel = b & 31;
+		b >>= 5;
+		pattern->n_entries++;
+		if (b) {
+			if (buflen + used[b] >= 65536) return -1;
+			dumbfile_getnc(buffer + buflen, used[b], f);
+			buflen += used[b];
+		} else {
+			/* End of row */
+			if (++pattern->n_rows == 64) break;
+			if (buflen >= 65536) return -1;
+		}
+#else
+		if (b == 0) {
+			/* End of row */
+			pattern->n_entries++;
+			if (++pattern->n_rows == 64) break;
+			if (buflen >= 65536) return -1;
+		} else {
+			static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
+			channel = b & 31;
+			b >>= 5;
+			if (b) {
+				pattern->n_entries++;
+				if (buflen + used[b] >= 65536) return -1;
+				dumbfile_getnc(buffer + buflen, used[b], f);
+				buflen += used[b];
+			}
+		}
+#endif
+
+		/* We have ensured that buflen < 65536 at this point, so it is safe
+		 * to iterate and read at least one more byte without checking.
+		 * However, now would be a good time to check for errors reading from
+		 * the file.
+		 */
+
+		if (dumbfile_error(f))
+			return -1;
+
+		/* Great. We ran out of data, but there should be data for more rows.
+		 * Fill the rest with null data...
+		 */
+		if (buflen >= length && pattern->n_rows < 64)
+		{
+			while (pattern->n_rows < 64)
+			{
+				if (buflen >= 65536) return -1;
+				buffer[buflen++] = 0;
+				pattern->n_entries++;
+				pattern->n_rows++;
+			}
+			break;
+		}
+	}
+
+	pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+
+	if (!pattern->entry)
+		return -1;
+
+	entry = pattern->entry;
+
+	while (bufpos < buflen) {
+		unsigned char b = buffer[bufpos++];
+
+#if 1
+		if (!(b & ~31))
+#else
+		if (b == 0)
+#endif
+		{
+			/* End of row */
+			IT_SET_END_ROW(entry);
+			entry++;
+			continue;
+		}
+
+		channel = b & 31;
+
+		if (b & 224) {
+			entry->mask = 0;
+			entry->channel = channel;
+
+			if (b & 32) {
+				unsigned char n = buffer[bufpos++];
+				if (n != 255) {
+					if (n == 254)
+						entry->note = IT_NOTE_CUT;
+					else
+						entry->note = (n >> 4) * 12 + (n & 15);
+					entry->mask |= IT_ENTRY_NOTE;
+				}
+
+				entry->instrument = buffer[bufpos++];
+				if (entry->instrument)
+					entry->mask |= IT_ENTRY_INSTRUMENT;
+			}
+
+			if (b & 64) {
+				entry->volpan = buffer[bufpos++];
+				if (entry->volpan != 255)
+					entry->mask |= IT_ENTRY_VOLPAN;
+			}
+
+			if (b & 128) {
+				entry->effect = buffer[bufpos++];
+				entry->effectvalue = buffer[bufpos++];
+				if (entry->effect != 255) {
+					entry->mask |= IT_ENTRY_EFFECT;
+					switch (entry->effect) {
+					case IT_BREAK_TO_ROW:
+						entry->effectvalue -= (entry->effectvalue >> 4) * 6;
+						break;
+
+					case IT_SET_CHANNEL_VOLUME:
+					case IT_CHANNEL_VOLUME_SLIDE:
+					case IT_PANNING_SLIDE:
+					case IT_GLOBAL_VOLUME_SLIDE:
+					case IT_PANBRELLO:
+					case IT_MIDI_MACRO:
+						entry->mask &= ~IT_ENTRY_EFFECT;
+						break;
+
+					case IT_S:
+						switch (entry->effectvalue >> 4) {
+						case IT_S_SET_PANBRELLO_WAVEFORM:
+						case IT_S_FINE_PATTERN_DELAY:
+						case IT_S7:
+						case IT_S_SET_SURROUND_SOUND:
+						case IT_S_SET_MIDI_MACRO:
+							entry->mask &= ~IT_ENTRY_EFFECT;
+							break;
+						}
+						break;
+					}
+				}
+				/** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */
+			}
+
+			entry++;
+		}
+	}
+
+	ASSERT(entry == pattern->entry + pattern->n_entries);
+
+	return 0;
+}
+
+
+
+/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */
+/* Currently we assume the sample data are stored after the sample headers in
+ * module files. This assumption may be unjustified; let me know if you have
+ * trouble.
+ */
+
+#define IT_COMPONENT_INSTRUMENT 1
+#define IT_COMPONENT_PATTERN    2
+#define IT_COMPONENT_SAMPLE     3
+
+typedef struct IT_COMPONENT
+{
+	unsigned char type;
+	unsigned char n;
+	long offset;
+	short sampfirst; /* component[sampfirst] = first sample data after this */
+	short sampnext; /* sampnext is used to create linked lists of sample data */
+}
+IT_COMPONENT;
+
+
+
+static int it_component_compare(const void *e1, const void *e2)
+{
+	return ((const IT_COMPONENT *)e1)->offset -
+	       ((const IT_COMPONENT *)e2)->offset;
+}
+
+
+
+static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv)
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	int flags, ffi;
+	int default_pan_present;
+
+	int master_volume;
+
+	unsigned char sample_pack[256];
+
+	IT_COMPONENT *component;
+	int n_components = 0;
+
+	int n;
+
+	unsigned char *buffer;
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata) return NULL;
+
+	/* Skip song name. */
+	dumbfile_getnc(sigdata->name, 28, f);
+	sigdata->name[28] = 0;
+
+	n = dumbfile_getc(f);
+
+	if (n != 0x1A && n != 0) {
+		free(sigdata);
+		return NULL;
+	}
+
+	if (dumbfile_getc(f) != 16) {
+		free(sigdata);
+		return NULL;
+	}
+
+	dumbfile_skip(f, 2);
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_orders = dumbfile_igetw(f);
+	sigdata->n_instruments = 0;
+	sigdata->n_samples = dumbfile_igetw(f);
+	sigdata->n_patterns = dumbfile_igetw(f);
+
+	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	sigdata->order = malloc(sigdata->n_orders);
+	if (!sigdata->order) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	if (sigdata->n_samples) {
+		sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
+		if (!sigdata->sample) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (n = 0; n < sigdata->n_samples; n++)
+			sigdata->sample[n].right = sigdata->sample[n].left = NULL;
+	}
+
+	if (sigdata->n_patterns) {
+		sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+		if (!sigdata->pattern) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (n = 0; n < sigdata->n_patterns; n++)
+			sigdata->pattern[n].entry = NULL;
+	}
+
+	flags = dumbfile_igetw(f);
+
+	*cwtv = dumbfile_igetw(f);
+
+	if (*cwtv == 0x1300) {
+		/** WARNING: volume slides on every frame */
+	}
+
+	ffi = dumbfile_igetw(f);
+
+	/** WARNING: which ones? */
+	sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M;
+
+	if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	sigdata->global_volume = dumbfile_getc(f) << 1;
+	sigdata->speed = dumbfile_getc(f);
+	if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
+	sigdata->tempo = dumbfile_getc(f);
+	master_volume = dumbfile_getc(f); // 7 bits; +128 for stereo
+	//what do we do with master_volume? it's not the same as mixing volume...
+	sigdata->mixing_volume = 48;
+
+	if (master_volume & 128) sigdata->flags |= IT_STEREO;
+
+	/* Skip GUS Ultra Click Removal byte. */
+	dumbfile_getc(f);
+
+	default_pan_present = dumbfile_getc(f);
+
+	dumbfile_skip(f, 8);
+
+	/* Skip Special Custom Data Pointer. */
+	/** WARNING: investigate this? */
+	dumbfile_igetw(f);
+
+	sigdata->n_pchannels = 0;
+	/* Channel settings for 32 channels, 255=unused, +128=disabled */
+	{
+		int i;
+		for (i = 0; i < 32; i++) {
+			int c = dumbfile_getc(f);
+			if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */
+				if (sigdata->n_pchannels < i + 1) sigdata->n_pchannels = i + 1;
+				sigdata->channel_volume[i] = 64;
+				sigdata->channel_pan[i] = c & 8 ? 12 : 3;
+				/** WARNING: ah, but it should be 7 for mono... */
+			} else {
+				/** WARNING: this could be improved if we support channel muting... */
+				sigdata->channel_volume[i] = 0;
+				sigdata->channel_pan[i] = 7;
+			}
+		}
+	}
+
+	/* Orders, byte each, length = sigdata->n_orders (should be even) */
+	dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+	sigdata->restart_position = 0;
+
+	component = malloc(768*sizeof(*component));
+	if (!component) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	for (n = 0; n < sigdata->n_samples; n++) {
+		component[n_components].type = IT_COMPONENT_SAMPLE;
+		component[n_components].n = n;
+		component[n_components].offset = dumbfile_igetw(f) << 4;
+		component[n_components].sampfirst = -1;
+		n_components++;
+	}
+
+	for (n = 0; n < sigdata->n_patterns; n++) {
+		long offset = dumbfile_igetw(f) << 4;
+		if (offset) {
+			component[n_components].type = IT_COMPONENT_PATTERN;
+			component[n_components].n = n;
+			component[n_components].offset = offset;
+			component[n_components].sampfirst = -1;
+			n_components++;
+		} else {
+			/** WARNING: Empty 64-row pattern ... ? (this does happen!) */
+			sigdata->pattern[n].n_rows = 64;
+			sigdata->pattern[n].n_entries = 0;
+		}
+	}
+
+	qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
+
+	/* I found a really dumb S3M file that claimed to contain default pan
+	 * data but didn't contain any. Programs would load it by reading part of
+	 * the first instrument header, assuming the data to be default pan
+	 * positions, and then rereading the instrument module. We cannot do this
+	 * without obfuscating the file input model, so we insert an extra check
+	 * here that we won't overrun the start of the first component.
+	 */
+	if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) {
+		/* Channel default pan positions */
+		int i;
+		for (i = 0; i < 32; i++) {
+			int c = dumbfile_getc(f);
+			if (c & 32)
+				sigdata->channel_pan[i] = c & 15;
+		}
+	}
+
+	{
+		int i;
+		for (i = 0; i < 32; i++) {
+			sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3;
+			sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7;
+		}
+	}
+
+	sigdata->pan_separation = 128;
+
+	if (dumbfile_error(f)) {
+		free(component);
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	buffer = malloc(65536);
+	if (!buffer) {
+		free(component);
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	/* Voila, I must deal with a very dumb S3M myself. This file refers to the same file offset twice
+	 * for two different patterns. Solution: Eliminate it.
+	 */
+
+	for (n = 0; n < n_components; n++) {
+		if (component[n].type == IT_COMPONENT_PATTERN) {
+			int m;
+			for (m = n + 1; m < n_components; m++) {
+				if (component[m].type == IT_COMPONENT_PATTERN) {
+					if (component[n].offset == component[m].offset) {
+						int o, pattern;
+						pattern = component[m].n;
+						n_components--;
+						for (o = m; o < n_components; o++) {
+							component[o] = component[o + 1];
+						}
+						for (o = 0; o < sigdata->n_orders; o++) {
+							if (sigdata->order[o] == pattern) {
+								sigdata->order[o] = component[n].n;
+							}
+						}
+						sigdata->pattern[pattern].n_rows = 64;
+						sigdata->pattern[pattern].n_entries = 0;
+						m--;
+					} else
+						break;
+				}
+			}
+		}
+	}
+
+	for (n = 0; n < n_components; n++) {
+		long offset;
+		int m;
+
+		if (it_seek(f, component[n].offset)) {
+			free(buffer);
+			free(component);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+
+		switch (component[n].type) {
+
+			case IT_COMPONENT_PATTERN:
+				if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+				break;
+
+			case IT_COMPONENT_SAMPLE:
+				if (it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f)) {
+					free(buffer);
+					free(component);
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+
+				if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
+					short *sample;
+
+					for (m = n + 1; m < n_components; m++)
+						if (component[m].offset > offset)
+							break;
+					m--;
+
+					sample = &component[m].sampfirst;
+
+					while (*sample >= 0 && component[*sample].offset <= offset)
+						sample = &component[*sample].sampnext;
+
+					component[n].sampnext = *sample;
+					*sample = n;
+
+					component[n].offset = offset;
+				}
+		}
+
+		m = component[n].sampfirst;
+
+		while (m >= 0) {
+			if (it_seek(f, component[m].offset)) {
+				free(buffer);
+				free(component);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+
+			if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, sample_pack[component[n].n], f)) {
+				free(buffer);
+				free(component);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+
+			m = component[m].sampnext;
+		}
+	}
+
+	free(buffer);
+	free(component);
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	return sigdata;
+}
+
+static char hexdigit(int in)
+{
+	if (in < 10) return in + '0';
+	else return in + 'A' - 10;
+}
+
+DUH *dumb_read_s3m(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+	int cwtv;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_s3m_load_sigdata(f, &cwtv);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		char version[8];
+		const char *tag[3][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "S3M";
+		tag[2][0] = "TRACKERVERSION";
+		version[0] = hexdigit((cwtv >> 8) & 15);
+		version[1] = '.';
+		version[2] = hexdigit((cwtv >> 4) & 15);
+		version[3] = hexdigit(cwtv & 15);
+		version[4] = 0;
+		tag[2][1] = (const char *) &version;
+		return make_duh(length, 3, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readxm.c
@@ -1,0 +1,1051 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readxm.c - Code to read a Fast Tracker II          / / \  \
+ *            module from an open file.              | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Julien Cugniere. Some bits of code taken        \_  /  > /
+ * from reads3m.c.                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/** TODO:
+
+ * XM_TREMOLO                        doesn't sound quite right...
+ * XM_E_SET_FINETUNE                 todo.
+ * XM_SET_ENVELOPE_POSITION          todo.
+
+ * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check
+   that effect memory is correct when using XM_VOLSLIDE_VIBRATO.
+   - sample vibrato (instrument vibrato) is now handled correctly. - entheh
+
+ * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when
+   a new instrument is played. In retrigger_note()?. Is it worth implementing?
+
+ * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all.
+
+ * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader.
+
+ * A lot of things need to be reset when the end of the song is reached.
+
+ * It seems that IT and XM don't behave the same way when dealing with
+   mixed loops. When IT encounters multiple SBx (x>0) commands on the same
+   row, it decrements the loop count for all, but only execute the loop of
+   the last one (highest channel). FT2 only decrements the loop count of the
+   last one. Not that I know of any modules using so convoluted combinations!
+
+ * Maybe we could remove patterns that don't appear in the order table ? Or
+   provide a function to "optimize" a DUMB_IT_SIGDATA ?
+
+*/
+
+
+
+#define XM_LINEAR_FREQUENCY        1 /* otherwise, use amiga slides */
+
+#define XM_ENTRY_PACKED            128
+#define XM_ENTRY_NOTE              1
+#define XM_ENTRY_INSTRUMENT        2
+#define XM_ENTRY_VOLUME            4
+#define XM_ENTRY_EFFECT            8
+#define XM_ENTRY_EFFECTVALUE       16
+
+#define XM_NOTE_OFF                97
+
+#define XM_ENVELOPE_ON             1
+#define XM_ENVELOPE_SUSTAIN        2
+#define XM_ENVELOPE_LOOP           4
+
+#define XM_SAMPLE_NO_LOOP          0
+#define XM_SAMPLE_FORWARD_LOOP     1
+#define XM_SAMPLE_PINGPONG_LOOP    2
+#define XM_SAMPLE_16BIT            16
+#define XM_SAMPLE_STEREO           32
+
+#define XM_VIBRATO_SINE            0
+#define XM_VIBRATO_SQUARE          1
+#define XM_VIBRATO_RAMP_DOWN       2
+#define XM_VIBRATO_RAMP_UP         3
+
+
+
+/* Probably useless :) */
+const char xm_convert_vibrato[] = {
+	IT_VIBRATO_SINE,
+	IT_VIBRATO_XM_SQUARE,
+	IT_VIBRATO_RAMP_DOWN,
+	IT_VIBRATO_RAMP_UP
+};
+
+
+
+#define XM_MAX_SAMPLES_PER_INSTRUMENT 16
+
+
+
+/* Extra data that doesn't fit inside IT_INSTRUMENT */
+typedef struct XM_INSTRUMENT_EXTRA
+{
+	int n_samples;
+	int vibrato_type;
+	int vibrato_sweep; /* 0-0xFF */
+	int vibrato_depth; /* 0-0x0F */
+	int vibrato_speed; /* 0-0x3F */
+}
+XM_INSTRUMENT_EXTRA;
+
+
+
+/* Frees the original block if it can't resize it or if size is 0, and acts
+ * as malloc if ptr is NULL.
+ */
+static void *safe_realloc(void *ptr, size_t size)
+{
+	if (ptr == NULL)
+		return malloc(size);
+
+	if (size == 0) {
+		free(ptr);
+		return NULL;
+	} else {
+		void *new_block = realloc(ptr, size);
+		if (!new_block)
+			free(ptr);
+		return new_block;
+	}
+}
+
+
+
+/* The interpretation of the XM volume column is left to the player. Here, we
+ * just filter bad values.
+ */
+// This function is so tiny now, should we inline it?
+static void it_xm_convert_volume(int volume, IT_ENTRY *entry)
+{
+	entry->mask |= IT_ENTRY_VOLPAN;
+	entry->volpan = volume;
+
+	switch (volume >> 4) {
+		case 0xA: /* set vibrato speed */
+		case 0xB: /* vibrato */
+		case 0xF: /* tone porta */
+		case 0x6: /* vol slide up */
+		case 0x7: /* vol slide down */
+		case 0x8: /* fine vol slide up */
+		case 0x9: /* fine vol slide down */
+		case 0xC: /* set panning */
+		case 0xD: /* pan slide left */
+		case 0xE: /* pan slide right */
+		case 0x1: /* set volume */
+		case 0x2: /* set volume */
+		case 0x3: /* set volume */
+		case 0x4: /* set volume */
+			break;
+
+		case 0x5:
+			if (volume == 0x50)
+				break; /* set volume */
+			/* else fall through */
+
+		default:
+			entry->mask &= ~IT_ENTRY_VOLPAN;
+			break;
+	}
+}
+
+
+
+static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer)
+{
+	int size;
+	int pos;
+	int channel;
+	int row;
+	int effect, effectvalue;
+	IT_ENTRY *entry;
+
+	/* pattern header size */
+	if (dumbfile_igetl(f) != 0x09) {
+		TRACE("XM error: unexpected pattern header size\n");
+		return -1;
+	}
+
+	/* pattern data packing type */
+	if (dumbfile_getc(f) != 0) {
+		TRACE("XM error: unexpected pattern packing type\n");
+		return -1;
+	}
+
+	pattern->n_rows = dumbfile_igetw(f);  /* 1..256 */
+	size = dumbfile_igetw(f);
+	pattern->n_entries = 0;
+
+	if (dumbfile_error(f))
+		return -1;
+
+	if (size == 0)
+		return 0;
+
+	if (size > 1280 * n_channels) {
+		TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels);
+		return -1;
+	}
+
+	if (dumbfile_getnc(buffer, size, f) < size)
+		return -1;
+
+	/* compute number of entries */
+	pattern->n_entries = 0;
+	pos = channel = row = 0;
+	while (pos < size) {
+		if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31))
+			pattern->n_entries++;
+
+		channel++;
+		if (channel >= n_channels) {
+			channel = 0;
+			row++;
+			pattern->n_entries++;
+		}
+
+		if (buffer[pos] & XM_ENTRY_PACKED) {
+			static const char offset[] = { 0, 1, 1, 2, 1, 2, 2, 3,   1, 2, 2, 3, 2, 3, 3, 4,
+			                               1, 2, 2, 3, 2, 3, 3, 4,   2, 3, 3, 4, 3, 4, 4, 5 };
+			pos += 1 + offset[buffer[pos] & 31];
+		} else {
+			pos += 5;
+		}
+	}
+
+	if (row > pattern->n_rows) {
+		TRACE("XM error: wrong number of rows in pattern data\n");
+		return -1;
+	}
+
+	/* Whoops, looks like some modules may be short, a few channels, maybe even rows... */
+
+	while (row < pattern->n_rows)
+	{
+		pattern->n_entries++;
+		row++;
+	}
+
+	pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
+	if (!pattern->entry)
+		return -1;
+
+	/* read the entries */
+	entry = pattern->entry;
+	pos = channel = row = 0;
+	while (pos < size) {
+		unsigned char mask;
+
+		if (buffer[pos] & XM_ENTRY_PACKED)
+			mask = buffer[pos++] & 31;
+		else
+			mask = 31;
+
+		if (mask) {
+			ASSERT(entry < pattern->entry + pattern->n_entries);
+
+			entry->channel = channel;
+			entry->mask = 0;
+
+			if (mask & XM_ENTRY_NOTE) {
+				int note = buffer[pos++]; /* 1-96 <=> C0-B7 */
+				entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note-1);
+				entry->mask |= IT_ENTRY_NOTE;
+			}
+
+			if (mask & XM_ENTRY_INSTRUMENT) {
+				entry->instrument = buffer[pos++]; /* 1-128 */
+				entry->mask |= IT_ENTRY_INSTRUMENT;
+			}
+
+			if (mask & XM_ENTRY_VOLUME)
+				it_xm_convert_volume(buffer[pos++], entry);
+
+			effect = effectvalue = 0;
+			if (mask & XM_ENTRY_EFFECT)      effect = buffer[pos++];
+			if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++];
+			_dumb_it_xm_convert_effect(effect, effectvalue, entry, 0);
+
+			entry++;
+		}
+
+		channel++;
+		if (channel >= n_channels) {
+			channel = 0;
+			row++;
+			IT_SET_END_ROW(entry);
+			entry++;
+		}
+	}
+
+	while (row < pattern->n_rows)
+	{
+		row++;
+		IT_SET_END_ROW(entry);
+		entry++;
+	}
+
+	return 0;
+}
+
+
+
+static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset)
+{
+	int i, pos;
+
+	if (envelope->n_nodes > 12) {
+		TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes);
+		envelope->n_nodes = 0;
+		return -1;
+	}
+
+	pos = 0;
+	for (i = 0; i < envelope->n_nodes; i++) {
+		envelope->node_t[i] = data[pos++];
+		if (data[pos] > 64) {
+			TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]);
+			envelope->n_nodes = 0;
+			return -1;
+		}
+		envelope->node_y[i] = (signed char)(data[pos++] + y_offset);
+	}
+
+	return 0;
+}
+
+
+
+static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f)
+{
+	unsigned long size, bytes_read;
+	unsigned short vol_points[24];
+	unsigned short pan_points[24];
+	int i, type;
+
+	/* Header size. Tends to be more than the actual size of the structure.
+	 * So unread bytes must be skipped before reading the first sample
+	 * header.
+	 */
+	size = dumbfile_igetl(f);
+
+	dumbfile_getnc(instrument->name, 22, f);
+	instrument->name[22] = 0;
+	instrument->filename[0] = 0;
+	dumbfile_skip(f, 1);  /* Instrument type. Should be 0, but seems random. */
+	extra->n_samples = dumbfile_igetw(f);
+
+	if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT)
+		return -1;
+
+	bytes_read = 4 + 22 + 1 + 2;
+
+	if (extra->n_samples) {
+		/* sample header size */
+		if (dumbfile_igetl(f) != 0x28) {
+			TRACE("XM error: unexpected sample header size\n");
+			return -1;
+		}
+
+		/* sample map */
+		for (i = 0; i < 96; i++) {
+			instrument->map_sample[i] = dumbfile_getc(f) + 1;
+			instrument->map_note[i] = i;
+		}
+
+		if (dumbfile_error(f))
+			return 1;
+
+		/* volume/panning envelopes */
+		for (i = 0; i < 24; i++)
+			vol_points[i] = dumbfile_igetw(f);
+		for (i = 0; i < 24; i++)
+			pan_points[i] = dumbfile_igetw(f);
+
+		instrument->volume_envelope.n_nodes = dumbfile_getc(f);
+		instrument->pan_envelope.n_nodes = dumbfile_getc(f);
+
+		if (dumbfile_error(f))
+			return -1;
+
+		instrument->volume_envelope.sus_loop_start = dumbfile_getc(f);
+		instrument->volume_envelope.loop_start = dumbfile_getc(f);
+		instrument->volume_envelope.loop_end = dumbfile_getc(f);
+
+		instrument->pan_envelope.sus_loop_start = dumbfile_getc(f);
+		instrument->pan_envelope.loop_start = dumbfile_getc(f);
+		instrument->pan_envelope.loop_end = dumbfile_getc(f);
+
+		/* The envelope handler for XM files won't use sus_loop_end. */
+
+		type = dumbfile_getc(f);
+		instrument->volume_envelope.flags = 0;
+		if ((type & XM_ENVELOPE_ON) && instrument->volume_envelope.n_nodes)
+			instrument->volume_envelope.flags |= IT_ENVELOPE_ON;
+		if (type & XM_ENVELOPE_LOOP)    instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON;
+#if 1
+		if (type & XM_ENVELOPE_SUSTAIN) instrument->volume_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP;
+#else // This is now handled in itrender.c
+		/* let's avoid fading out when reaching the last envelope node */
+		if (!(type & XM_ENVELOPE_LOOP)) {
+			instrument->volume_envelope.loop_start = instrument->volume_envelope.n_nodes-1;
+			instrument->volume_envelope.loop_end = instrument->volume_envelope.n_nodes-1;
+		}
+		instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON;
+#endif
+
+		type = dumbfile_getc(f);
+		instrument->pan_envelope.flags = 0;
+		if ((type & XM_ENVELOPE_ON) && instrument->pan_envelope.n_nodes)
+			instrument->pan_envelope.flags |= IT_ENVELOPE_ON;
+		if (type & XM_ENVELOPE_LOOP)    instrument->pan_envelope.flags |= IT_ENVELOPE_LOOP_ON; // should this be here?
+		if (type & XM_ENVELOPE_SUSTAIN) instrument->pan_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP;
+
+		if (it_xm_make_envelope(&instrument->volume_envelope, vol_points, 0) != 0) {
+			TRACE("XM error: volume envelope\n");
+			if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) return -1;
+		}
+
+		if (it_xm_make_envelope(&instrument->pan_envelope, pan_points, -32) != 0) {
+			TRACE("XM error: pan envelope\n");
+			if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) return -1;
+		}
+
+		instrument->pitch_envelope.flags = 0;
+
+		extra->vibrato_type = dumbfile_getc(f);
+		extra->vibrato_sweep = dumbfile_getc(f);
+		extra->vibrato_depth = dumbfile_getc(f);
+		extra->vibrato_speed = dumbfile_getc(f);
+
+		if (dumbfile_error(f) || extra->vibrato_type >= 4)
+			return -1;
+
+		/** WARNING: lossy approximation */
+		instrument->fadeout = (dumbfile_igetw(f)*128 + 64)/0xFFF;
+
+		dumbfile_skip(f, 2); /* reserved */
+
+		bytes_read += 4 + 96 + 48 + 48 + 14*1 + 2 + 2;
+	} else
+		for (i = 0; i < 96; i++)
+			instrument->map_sample[i] = 0;
+
+	if (dumbfile_skip(f, size - bytes_read))
+		return -1;
+
+	instrument->new_note_action = NNA_NOTE_CUT;
+	instrument->dup_check_type = DCT_OFF;
+	instrument->dup_check_action = DCA_NOTE_CUT;
+	instrument->pp_separation = 0;
+	instrument->pp_centre = 60; /* C-5 */
+	instrument->global_volume = 128;
+	instrument->default_pan = 32;
+	instrument->random_volume = 0;
+	instrument->random_pan = 0;
+	instrument->filter_cutoff = 0;
+	instrument->filter_resonance = 0;
+
+	return 0;
+}
+
+
+
+/* I (entheh) have two XM files saved by a very naughty program. After a
+ * 16-bit sample, it saved a rogue byte. The length of the sample was indeed
+ * an odd number, incremented to include the rogue byte.
+ *
+ * In this function we are converting sample lengths and loop points so they
+ * are measured in samples. This means we forget about the extra bytes, and
+ * they don't get skipped. So we fail trying to read the next instrument.
+ *
+ * To get around this, this function returns the number of rogue bytes that
+ * won't be accounted for by reading sample->length samples. It returns a
+ * negative number on failure.
+ */
+static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
+{
+	int type;
+	int relative_note_number; /* relative to C4 */
+	int finetune;
+	int roguebytes;
+	int roguebytesmask;
+	int reserved;
+
+	sample->length         = dumbfile_igetl(f);
+	sample->loop_start     = dumbfile_igetl(f);
+	sample->loop_end       = sample->loop_start + dumbfile_igetl(f);
+	sample->global_volume  = 64;
+	sample->default_volume = dumbfile_getc(f);
+	finetune               = (signed char)dumbfile_getc(f); /* -128..127 <=> -1 semitone .. +127/128 of a semitone */
+	type                   = dumbfile_getc(f);
+	sample->default_pan    = dumbfile_getc(f); /* 0-255 */
+	relative_note_number   = (signed char)dumbfile_getc(f);
+
+	/*dumbfile_skip(f, 1);  /* reserved */
+	reserved = dumbfile_getc(f);
+
+	dumbfile_getnc(sample->name, 22, f);
+	sample->name[22] = 0;
+
+	sample->filename[0] = 0;
+
+	if (dumbfile_error(f))
+		return -1;
+
+	sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number)*pow(DUMB_PITCH_BASE, finetune*2));
+
+	sample->flags = IT_SAMPLE_EXISTS;
+
+	if (reserved == 0xAD &&
+		(!(type & (XM_SAMPLE_16BIT | XM_SAMPLE_STEREO))))
+	{
+		/* F U Olivier Lapicque */
+		roguebytes = 4;
+		roguebytesmask = 4 << 2;
+	}
+	else
+	{
+		roguebytes = (int)sample->length;
+		roguebytesmask = 3;
+	}
+
+	if (type & XM_SAMPLE_16BIT) {
+		sample->flags |= IT_SAMPLE_16BIT;
+		sample->length >>= 1;
+		sample->loop_start >>= 1;
+		sample->loop_end >>= 1;
+	} else
+		roguebytesmask >>= 1;
+
+	if (type & XM_SAMPLE_STEREO) {
+		sample->flags |= IT_SAMPLE_STEREO;
+		sample->length >>= 1;
+		sample->loop_start >>= 1;
+		sample->loop_end >>= 1;
+	} else
+		roguebytesmask >>= 1;
+
+	roguebytes &= roguebytesmask;
+
+	if ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end) {
+		if (type & XM_SAMPLE_FORWARD_LOOP) sample->flags |= IT_SAMPLE_LOOP;
+		if (type & XM_SAMPLE_PINGPONG_LOOP) sample->flags |= IT_SAMPLE_LOOP | IT_SAMPLE_PINGPONG_LOOP;
+	}
+
+	if (sample->length <= 0)
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+	else if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
+		sample->flags &= ~IT_SAMPLE_LOOP;
+	else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
+		sample->flags &= ~IT_SAMPLE_LOOP;
+
+	return roguebytes;
+}
+
+
+
+static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f)
+{
+	int old;
+	long i;
+	long truncated_size;
+
+	if (!(sample->flags & IT_SAMPLE_EXISTS))
+		return dumbfile_skip(f, roguebytes);
+
+	/* let's get rid of the sample data coming after the end of the loop */
+	if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length && roguebytes != 4) {
+		truncated_size = sample->length - sample->loop_end;
+		sample->length = sample->loop_end;
+	} else {
+		truncated_size = 0;
+	}
+
+	sample->left = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+	if (!sample->left)
+		return -1;
+
+	if (sample->flags & IT_SAMPLE_STEREO) {
+		sample->right = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
+		if (!sample->right)
+			return -1;
+	}
+
+	if (roguebytes == 4)
+	{
+		if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0)
+			return -1;
+		roguebytes = 0;
+	}
+	else
+	{
+		/* sample data is stored as signed delta values */
+		old = 0;
+		if (sample->flags & IT_SAMPLE_16BIT)
+			for (i = 0; i < sample->length; i++)
+				((short *)sample->left)[i] = old += dumbfile_igetw(f);
+		else
+			for (i = 0; i < sample->length; i++)
+				((signed char *)sample->left)[i] = old += dumbfile_getc(f);
+	}
+
+	/* skip truncated data */
+	dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size));
+
+	if (sample->flags & IT_SAMPLE_STEREO) {
+		old = 0;
+		if (sample->flags & IT_SAMPLE_16BIT)
+			for (i = 0; i < sample->length; i++)
+				((short *)sample->right)[i] = old += dumbfile_igetw(f);
+		else
+			for (i = 0; i < sample->length; i++)
+				((signed char *)sample->right)[i] = old += dumbfile_getc(f);
+
+		/* skip truncated data */
+		dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size));
+	}
+
+	dumbfile_skip(f, roguebytes);
+
+	if (dumbfile_error(f))
+		return -1;
+
+	return 0;
+}
+
+
+
+/* "Real programmers don't document. If it was hard to write,
+ *  it should be hard to understand."
+ *
+ * (Never trust the documentation provided with a tracker.
+ *  Real files are the only truth...)
+ */
+/*static*/ DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f)
+{
+	DUMB_IT_SIGDATA *sigdata;
+	char id_text[18];
+
+	int flags;
+	int n_channels;
+	int total_samples;
+	int i, j;
+
+	/* check ID text */
+	if (dumbfile_getnc(id_text, 17, f) < 17)
+		return NULL;
+	id_text[17] = 0;
+	if (strcmp(id_text, "Extended Module: ") != 0) {
+		TRACE("XM error: Not an Extended Module\n");
+		return NULL;
+	}
+
+	sigdata = malloc(sizeof(*sigdata));
+	if (!sigdata)
+		return NULL;
+
+	/* song name */
+	if (dumbfile_getnc(sigdata->name, 20, f) < 20) {
+		free(sigdata);
+		return NULL;
+	}
+	sigdata->name[20] = 0;
+
+	if (dumbfile_getc(f) != 0x1A) {
+		TRACE("XM error: 0x1A not found\n");
+		free(sigdata);
+		return NULL;
+	}
+
+	/* tracker name */
+	if (dumbfile_skip(f, 20)) {
+		free(sigdata);
+		return NULL;
+	}
+
+	/* version number */
+	if (dumbfile_igetw(f) != 0x0104) {
+		TRACE("XM error: wrong format version\n");
+		free(sigdata);
+		return NULL;
+	}
+
+	/*
+		------------------
+		---   Header   ---
+		------------------
+	*/
+
+	/* header size */
+	if (dumbfile_igetl(f) != 0x0114) {
+		TRACE("XM error: unexpected header size\n");
+		free(sigdata);
+		return NULL;
+	}
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->n_samples        = 0;
+	sigdata->n_orders         = dumbfile_igetw(f);
+	sigdata->restart_position = dumbfile_igetw(f);
+	n_channels                = dumbfile_igetw(f); /* max 32 but we'll be lenient */
+	sigdata->n_pchannels      = n_channels;
+	sigdata->n_patterns       = dumbfile_igetw(f);
+	sigdata->n_instruments    = dumbfile_igetw(f); /* max 128 */
+	flags                     = dumbfile_igetw(f);
+	sigdata->speed            = dumbfile_igetw(f);
+	if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
+	sigdata->tempo            = dumbfile_igetw(f);
+
+	/* sanity checks */
+	if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > 256 || sigdata->n_patterns > 256 || sigdata->n_instruments > 128 || n_channels > DUMB_IT_N_CHANNELS) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	//if (sigdata->restart_position >= sigdata->n_orders)
+		//sigdata->restart_position = 0;
+
+	/* order table */
+	sigdata->order = malloc(sigdata->n_orders*sizeof(*sigdata->order));
+	if (!sigdata->order) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
+	dumbfile_skip(f, 256 - sigdata->n_orders);
+
+	if (dumbfile_error(f)) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	/*
+		--------------------
+		---   Patterns   ---
+		--------------------
+	*/
+
+	sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
+	if (!sigdata->pattern) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+	for (i = 0; i < sigdata->n_patterns; i++)
+		sigdata->pattern[i].entry = NULL;
+
+	{
+		unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */
+		if (!buffer) {
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+		for (i = 0; i < sigdata->n_patterns; i++) {
+			if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) {
+				free(buffer);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+		}
+		free(buffer);
+	}
+
+	/*
+		-----------------------------------
+		---   Instruments and Samples   ---
+		-----------------------------------
+	*/
+
+	sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
+	if (!sigdata->instrument) {
+		_dumb_it_unload_sigdata(sigdata);
+		return NULL;
+	}
+
+	/* With XM, samples are not global, they're part of an instrument. In a
+	 * file, each instrument is stored with its samples. Because of this, I
+	 * don't know how to find how many samples are present in the file. Thus
+	 * I have to do n_instruments reallocation on sigdata->sample.
+	 * Looking at FT2, it doesn't seem possible to have more than 16 samples
+	 * per instrument (even though n_samples is stored as 2 bytes). So maybe
+	 * we could allocate a 128*16 array of samples, and shrink it back to the
+	 * correct size when we know it?
+	 * Alternatively, I could allocate samples by blocks of N (still O(n)),
+	 * or double the number of allocated samples when I need more (O(log n)).
+	 */
+	total_samples = 0;
+	sigdata->sample = NULL;
+
+	for (i = 0; i < sigdata->n_instruments; i++) {
+		XM_INSTRUMENT_EXTRA extra;
+
+		if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
+			TRACE("XM error: instrument %d\n", i+1);
+			_dumb_it_unload_sigdata(sigdata);
+			return NULL;
+		}
+
+		if (extra.n_samples) {
+			unsigned char roguebytes[XM_MAX_SAMPLES_PER_INSTRUMENT];
+
+			/* adjust instrument sample map (make indices absolute) */
+			for (j = 0; j < 96; j++)
+				sigdata->instrument[i].map_sample[j] += total_samples;
+
+			sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
+			if (!sigdata->sample) {
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+			for (j = total_samples; j < total_samples+extra.n_samples; j++)
+				sigdata->sample[j].right = sigdata->sample[j].left = NULL;
+
+			/* read instrument's samples */
+			for (j = 0; j < extra.n_samples; j++) {
+				IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
+				int b = it_xm_read_sample_header(sample, f);
+				if (b < 0) {
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+				roguebytes[j] = b;
+				// Any reason why these can't be set inside it_xm_read_sample_header()?
+				sample->vibrato_speed = extra.vibrato_speed;
+				sample->vibrato_depth = extra.vibrato_depth;
+				sample->vibrato_rate = extra.vibrato_sweep;
+				/* Rate and sweep don't match, but the difference is
+				 * accounted for in itrender.c.
+				 */
+				sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type];
+				sample->max_resampling_quality = -1;
+			}
+			for (j = 0; j < extra.n_samples; j++) {
+				if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
+					_dumb_it_unload_sigdata(sigdata);
+					return NULL;
+				}
+			}
+			total_samples += extra.n_samples;
+		}
+	}
+
+	sigdata->n_samples = total_samples;
+
+	sigdata->flags = IT_WAS_AN_XM | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_USE_INSTRUMENTS;
+	// Are we OK with IT_COMPATIBLE_GXX off?
+	//
+	// When specifying note + instr + tone portamento, and an old note is still playing (even after note off):
+	// - If Compatible Gxx is on, the new note will be triggered only if the instrument _changes_.
+	// - If Compatible Gxx is off, the new note will always be triggered, provided the instrument is specified.
+	// - FT2 seems to do the latter (unconfirmed).
+
+	// Err, wait. XM playback has its own code. The change made to the IT
+	// playbackc code didn't affect XM playback. Forget this then. There's
+	// still a bug in XM playback though, and it'll need some investigation...
+	// tomorrow...
+
+	// UPDATE: IT_COMPATIBLE_GXX is required to be on, so that tone porta has
+	// separate memory from portamento.
+
+	if (flags & XM_LINEAR_FREQUENCY)
+		sigdata->flags |= IT_LINEAR_SLIDES;
+
+	sigdata->global_volume = 128;
+	sigdata->mixing_volume = 48;
+	sigdata->pan_separation = 128;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+	memset(sigdata->channel_pan, 32, DUMB_IT_N_CHANNELS);
+
+	_dumb_it_fix_invalid_orders(sigdata);
+
+	return sigdata;
+}
+
+
+
+#if 0 // no fucking way, dude!
+
+/* The length returned is the time required to play from the beginning of the
+ * file to the last row of the last order (which is when the player will
+ * loop). Depending on the song, the sound might stop sooner.
+ * Due to fixed point roundoffs, I think this is only reliable to the second.
+ * Full precision could be achieved by using a double during the computation,
+ * or maybe a LONG_LONG.
+ */
+long it_compute_length(const DUMB_IT_SIGDATA *sigdata)
+{
+	IT_PATTERN *pattern;
+	int tempo, speed;
+	int loop_start[IT_N_CHANNELS];
+	char loop_count[IT_N_CHANNELS];
+	int order, entry;
+	int row_first_entry = 0;
+	int jump, jump_dest;
+	int delay, fine_delay;
+	int i;
+	long t;
+
+	if (!sigdata)
+		return 0;
+
+	tempo = sigdata->tempo;
+	speed = sigdata->speed;
+	order = entry = 0;
+	jump = jump_dest = 0;
+	t = 0;
+
+	/* for each PATTERN */
+	for (order = 0; order < sigdata->n_orders; order++) {
+		
+		if (sigdata->order[order] == IT_ORDER_END) break;
+		if (sigdata->order[order] == IT_ORDER_SKIP) continue;
+
+		for (i = 0; i < IT_N_CHANNELS; i++)
+			loop_count[i] = -1;
+
+		pattern = &sigdata->pattern[ sigdata->order[order] ];
+		entry = 0;
+		if (jump == IT_BREAK_TO_ROW) {
+			int row = 0;
+			while (row < jump_dest)
+				if (pattern->entry[entry++].channel >= IT_N_CHANNELS)
+					row++;
+		}
+
+		/* for each ROW */
+		while (entry < pattern->n_entries) {
+			row_first_entry = entry;
+			delay = fine_delay = 0;
+			jump = 0;
+
+			/* for each note NOTE */
+			while (entry < pattern->n_entries && pattern->entry[entry].channel < IT_N_CHANNELS) {
+				int value   = pattern->entry[entry].effectvalue;
+				int channel = pattern->entry[entry].channel;
+
+				switch (pattern->entry[entry].effect) {
+
+					case IT_SET_SPEED: speed = value; break;
+
+					case IT_JUMP_TO_ORDER:
+						if (value <= order) /* infinite loop */
+							return 0;
+						jump = IT_JUMP_TO_ORDER;
+						jump_dest = value;
+						break;
+
+					case IT_BREAK_TO_ROW:
+						jump = IT_BREAK_TO_ROW;
+						jump_dest = value;
+						break;
+
+					case IT_S:
+						switch (HIGH(value)) {
+							case IT_S_PATTERN_DELAY:      delay      = LOW(value); break;
+							case IT_S_FINE_PATTERN_DELAY: fine_delay = LOW(value); break;
+							case IT_S_PATTERN_LOOP:
+								if (LOW(value) == 0) {
+									loop_start[channel] = row_first_entry;
+								} else {
+									if (loop_count[channel] == -1)
+										loop_count[channel] = LOW(value);
+
+									if (loop_count[channel]) {
+										jump = IT_S_PATTERN_LOOP;
+										jump_dest = loop_start[channel];
+									}
+									loop_count[channel]--;
+								}
+								break;
+						}
+						break;
+
+					case IT_SET_SONG_TEMPO:
+						switch (HIGH(value)) { /* slides happen every non-row frames */
+							case 0:  tempo = tempo - LOW(value)*(speed-1); break;
+							case 1:  tempo = tempo + LOW(value)*(speed-1); break;
+							default: tempo = value;
+						}
+						tempo = MID(32, tempo, 255);
+						break;
+				}
+
+				entry++;
+			}
+
+			/* end of ROW */
+			entry++;
+			t += TICK_TIME_DIVIDEND * (speed*(1+delay) + fine_delay) / tempo;
+
+			if (jump == IT_JUMP_TO_ORDER) {
+				order = jump_dest - 1;
+				break;
+			} else if (jump == IT_BREAK_TO_ROW)
+				break;
+			else if (jump == IT_S_PATTERN_LOOP)
+				entry = jump_dest - 1;
+		}
+
+		/* end of PATTERN */
+	}
+
+	return t;
+}
+
+#endif /* 0 */
+
+
+
+DUH *dumb_read_xm(DUMBFILE *f)
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_xm_load_sigdata(f);
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "XM";
+		return make_duh(length, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
+	}
+}
--- /dev/null
+++ b/dumb/src/it/xmeffect.c
@@ -1,0 +1,242 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * xmeffect.c - Code for converting MOD/XM            / / \  \
+ *              effects to IT effects.               | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Julien Cugniere. Ripped out of readxm.c         \_  /  > /
+ * by entheh.                                           | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+
+#if 0
+unsigned char **_dumb_malloc2(int w, int h)
+{
+	unsigned char **line =  malloc(h * sizeof(*line));
+	int i;
+	if (!line) return NULL;
+
+	line[0] = malloc(w * h * sizeof(*line[0]));
+	if (!line[0]) {
+		free(line);
+		return NULL;
+	}
+
+	for (i = 1; i < h; i++)
+		line[i] = line[i-1] + w;
+
+	memset(line[0], 0, w*h);
+
+	return line;
+}
+
+
+
+void _dumb_free2(unsigned char **line)
+{
+	if (line) {
+		if (line[0])
+			free(line[0]);
+		free(line);
+	}
+}
+
+
+
+/* Effects having a memory. 2 means that the two parts of the effectvalue
+ * should be handled separately.
+ */
+static const char xm_has_memory[] = {
+/*	0  1  2  3  4  5  6  7  8  9  A  B  C  D (E) F  G  H        K  L           P     R     T          (X) */
+	0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+
+/*  E0 E1 E2 E3 E4 E5 E6 E7    E9 EA EB EC ED EE         X1 X2 */
+	0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0,   0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif
+
+
+
+/* Effects marked with 'special' are handled specifically in itrender.c */
+void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod)
+{
+const int log = 0;
+
+	if ((!effect && !value) || (effect >= XM_N_EFFECTS))
+		return;
+
+if (log) printf("%c%02X", (effect<10)?('0'+effect):('A'+effect-10), value);
+
+	/* Linearisation of the effect number... */
+	if (effect == XM_E) {
+		effect = EBASE + HIGH(value);
+		value = LOW(value);
+	} else if (effect == XM_X) {
+		effect = XBASE + HIGH(value);
+		value = LOW(value);
+	}
+
+if (log) printf(" - %2d %02X", effect, value);
+
+#if 0 // This should be handled in itrender.c!
+	/* update effect memory */
+	switch (xm_has_memory[effect]) {
+		case 1:
+			if (!value)
+				value = memory[entry->channel][effect];
+			else
+				memory[entry->channel][effect] = value;
+			break;
+
+		case 2:
+			if (!HIGH(value))
+				SET_HIGH(value, HIGH(memory[entry->channel][effect]));
+			else
+				SET_HIGH(memory[entry->channel][effect], HIGH(value));
+
+			if (!LOW(value))
+				SET_LOW(value, LOW(memory[entry->channel][effect]));
+			else
+				SET_LOW(memory[entry->channel][effect], LOW(value));
+			break;
+	}
+#endif
+
+	/* convert effect */
+	entry->mask |= IT_ENTRY_EFFECT;
+	switch (effect) {
+
+		case XM_APPREGIO:           effect = IT_ARPEGGIO;           break;
+		case XM_VIBRATO:            effect = IT_VIBRATO;            break;
+		case XM_TONE_PORTAMENTO:    effect = IT_TONE_PORTAMENTO;    break;
+		case XM_TREMOLO:            effect = IT_TREMOLO;            break;
+		case XM_SET_PANNING:        effect = IT_SET_PANNING;        break;
+		case XM_SAMPLE_OFFSET:      effect = IT_SET_SAMPLE_OFFSET;  break;
+		case XM_POSITION_JUMP:      effect = IT_JUMP_TO_ORDER;      break;
+		case XM_MULTI_RETRIG:       effect = IT_RETRIGGER_NOTE;     break;
+		case XM_TREMOR:             effect = IT_TREMOR;             break;
+		case XM_PORTAMENTO_UP:      effect = IT_XM_PORTAMENTO_UP;   break;
+		case XM_PORTAMENTO_DOWN:    effect = IT_XM_PORTAMENTO_DOWN; break;
+		case XM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; /* special */
+		case XM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; /* special */
+		case XM_VOLSLIDE_VIBRATO:   effect = IT_VOLSLIDE_VIBRATO;   break; /* special */
+
+		case XM_PATTERN_BREAK:
+			effect = IT_BREAK_TO_ROW;
+			value = BCD_TO_NORMAL(value);
+			if (value > 63) value = 0; /* FT2, maybe ProTracker? */
+			break;
+
+		case XM_VOLUME_SLIDE: /* special */
+			effect = IT_VOLUME_SLIDE;
+			value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
+			break;
+
+		case XM_PANNING_SLIDE:
+			effect = IT_PANNING_SLIDE;
+			//value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
+			value = HIGH(value) ? EFFECT_VALUE(0, HIGH(value)) : EFFECT_VALUE(LOW(value), 0);
+			break;
+
+		case XM_GLOBAL_VOLUME_SLIDE: /* special */
+			effect = IT_GLOBAL_VOLUME_SLIDE;
+			value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
+			break;
+
+		case XM_SET_TEMPO_BPM:
+			if (mod) effect = (value <= 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
+			else effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
+			break;
+
+		case XM_SET_GLOBAL_VOLUME:
+			effect = IT_SET_GLOBAL_VOLUME;
+			value *= 2;
+			break;
+
+		case XM_KEY_OFF:
+			/** WARNING: In FT2, the value seems to do something... Oh well,
+			 * this is undocumented anyway!
+			 */
+			effect = IT_XM_KEY_OFF;
+			break;
+
+		case EBASE+XM_E_SET_FILTER:            effect = SBASE+IT_S_SET_FILTER;            break;
+		case EBASE+XM_E_SET_GLISSANDO_CONTROL: effect = SBASE+IT_S_SET_GLISSANDO_CONTROL; break; /** TODO */
+		case EBASE+XM_E_SET_FINETUNE:          effect = SBASE+IT_S_FINETUNE;              break;
+		case EBASE+XM_E_SET_LOOP:              effect = SBASE+IT_S_PATTERN_LOOP;          break;
+		case EBASE+XM_E_NOTE_CUT:              effect = SBASE+IT_S_DELAYED_NOTE_CUT;      break;
+		case EBASE+XM_E_NOTE_DELAY:            effect = SBASE+IT_S_NOTE_DELAY;            break;
+		case EBASE+XM_E_PATTERN_DELAY:         effect = SBASE+IT_S_PATTERN_DELAY;         break;
+		case EBASE+XM_E_SET_PANNING:           effect = SBASE+IT_S_SET_PAN;               break;
+		case EBASE+XM_E_FINE_VOLSLIDE_UP:      effect = IT_XM_FINE_VOLSLIDE_UP;           break;
+		case EBASE+XM_E_FINE_VOLSLIDE_DOWN:    effect = IT_XM_FINE_VOLSLIDE_DOWN;         break;
+
+		case EBASE + XM_E_FINE_PORTA_UP:
+			effect = IT_PORTAMENTO_UP;
+			value = EFFECT_VALUE(0xF, value);
+			break;
+
+		case EBASE + XM_E_FINE_PORTA_DOWN:
+			effect = IT_PORTAMENTO_DOWN;
+			value = EFFECT_VALUE(0xF, value);
+			break;
+
+		case EBASE + XM_E_RETRIG_NOTE:
+			effect = IT_XM_RETRIGGER_NOTE;
+			value = EFFECT_VALUE(0, value);
+			break;
+
+		case EBASE + XM_E_SET_VIBRATO_CONTROL:
+			effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM;
+			value &= ~4;
+			break;
+
+		case EBASE + XM_E_SET_TREMOLO_CONTROL:
+			effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM;
+			value &= ~4;
+			break;
+
+		case XBASE + XM_X_EXTRAFINE_PORTA_UP:
+			effect = IT_PORTAMENTO_UP;
+			value = EFFECT_VALUE(0xE, value);
+			break;
+
+		case XBASE + XM_X_EXTRAFINE_PORTA_DOWN:
+			effect = IT_PORTAMENTO_DOWN;
+			value = EFFECT_VALUE(0xE, value);
+			break;
+
+		default:
+			/* user effect (often used in demos for synchronisation) */
+			entry->mask &= ~IT_ENTRY_EFFECT;
+	}
+
+if (log) printf(" - %2d %02X", effect, value);
+
+	/* Inverse linearisation... */
+	if (effect >= SBASE && effect < SBASE+16) {
+		value = EFFECT_VALUE(effect-SBASE, value);
+		effect = IT_S;
+	}
+
+if (log) printf(" - %c%02X\n", 'A'+effect-1, value);
+
+	entry->effect = effect;
+	entry->effectvalue = value;
+}
--- /dev/null
+++ b/dumb/vc6/dumb/dumb.vcproj
@@ -1,0 +1,1755 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="dumb"
+	ProjectGUID="{612D360C-A51B-4B34-8F49-33F42A2957F5}"
+	SccProjectName="&quot;$/foobar2000/plugins.root/plugins&quot;, CEAAAAAA"
+	SccLocalPath="..\..\..\.."
+	SccProvider="MSSCCI:Microsoft Visual SourceSafe"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\..\..\..\Program Files\Microsoft Visual Studio 8\VC\VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="2"
+				WholeProgramOptimization="true"
+				AdditionalIncludeDirectories="../../include"
+				PreprocessorDefinitions="NDEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED"
+				StringPooling="true"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				PrecompiledHeaderFile="$(IntDir)\$(ProjectName).pch"
+				AssemblerListingLocation="$(IntDir)\"
+				ObjectFile="$(IntDir)\"
+				ProgramDataBaseFileName="$(IntDir)\"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+				CompileAs="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="$(OutDir)\$(ProjectName).lib"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\..\..\..\Program Files\Microsoft Visual Studio 8\VC\VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="../../include"
+				PreprocessorDefinitions="_DEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED;DEBUGMODE=1"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				PrecompiledHeaderFile="$(IntDir)\$(ProjectName).pch"
+				AssemblerListingLocation="$(IntDir)\"
+				ObjectFile="$(IntDir)\"
+				ProgramDataBaseFileName="$(IntDir)\"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+				DebugInformationFormat="4"
+				CompileAs="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="$(OutDir)\$(ProjectName).lib"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release staticlink|Win32"
+			OutputDirectory="$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			InheritedPropertySheets="..\..\..\..\..\Program Files\Microsoft Visual Studio 8\VC\VCProjectDefaults\UpgradeFromVC71.vsprops"
+			UseOfMFC="0"
+			ATLMinimizesCRunTimeLibraryUsage="false"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				InlineFunctionExpansion="2"
+				WholeProgramOptimization="true"
+				AdditionalIncludeDirectories="../../include"
+				PreprocessorDefinitions="NDEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED"
+				StringPooling="true"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				PrecompiledHeaderFile="$(IntDir)\$(ProjectName).pch"
+				AssemblerListingLocation="$(IntDir)\"
+				ObjectFile="$(IntDir)\"
+				ProgramDataBaseFileName="$(IntDir)\"
+				WarningLevel="3"
+				SuppressStartupBanner="true"
+				CompileAs="0"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				OutputFile="$(OutDir)\$(ProjectName).lib"
+				SuppressStartupBanner="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="src"
+			>
+			<Filter
+				Name="core"
+				>
+				<File
+					RelativePath="..\..\src\core\atexit.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\duhlen.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\duhtag.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\dumbfile.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\loadduh.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\makeduh.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\rawsig.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\readduh.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\register.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\rendduh.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\rendsig.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\core\unload.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+			</Filter>
+			<Filter
+				Name="helpers"
+				>
+				<File
+					RelativePath="..\..\src\helpers\barray.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\helpers\clickrem.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\helpers\memfile.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\helpers\resample.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\helpers\sampbuf.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\helpers\silence.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\helpers\stdfile.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+			</Filter>
+			<Filter
+				Name="it"
+				>
+				<File
+					RelativePath="..\..\src\it\itload.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\itmisc.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\itorder.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\itread.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\itrender.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\itunload.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\load669.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\loadmod.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\loadmtm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\loadoldpsm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\loadpsm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\loads3m.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\loadxm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\ptmeffect.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\read669.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\readmod.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\readmtm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\readoldpsm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\readpsm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\readptm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\reads3m.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\readxm.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+				<File
+					RelativePath="..\..\src\it\xmeffect.c"
+					>
+					<FileConfiguration
+						Name="Release|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Debug|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="0"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+							BasicRuntimeChecks="3"
+						/>
+					</FileConfiguration>
+					<FileConfiguration
+						Name="Release staticlink|Win32"
+						>
+						<Tool
+							Name="VCCLCompilerTool"
+							Optimization="2"
+							AdditionalIncludeDirectories=""
+							PreprocessorDefinitions=""
+						/>
+					</FileConfiguration>
+				</File>
+			</Filter>
+		</Filter>
+		<Filter
+			Name="include"
+			>
+			<File
+				RelativePath="..\..\include\dumb.h"
+				>
+			</File>
+			<Filter
+				Name="internal"
+				>
+				<File
+					RelativePath="..\..\include\internal\dumb.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\include\internal\it.h"
+					>
+				</File>
+			</Filter>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
--- /dev/null
+++ b/dumb/vc6/dumb/dumb.vcproj.vspscc
@@ -1,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:dumb\\dumb\\vc6\\dumb"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
+}