/*
	wildmidi_lib.c

 	Midi Wavetable Processing library

 	Copyright (C)2001-2004 Chris Ison

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	Email: cisos@bigpond.net.au
		 wildcode@users.sourceforge.net

 	$Id: wildmidi_lib.c,v 1.18 2004/01/28 05:45:09 wildcode Exp $

	========================================
	Changelog
	----------------------------------------
	Aug 3, 2003
		- changed kill envelope from 6th to 5th
		- changed data_length, loop_start, loop_end to 22:10 fixed point
		- added fractional position to loop_start and loop_end
		- added fake reverb

	Aug 4, 2003
		- added MIDI_EVENT_DEBUG to midi event functions
		- fixed hold release killing off notes that hadn't been turned off
		  by note off
		- sped up fake reverb by doing it outside the channel mixing loop

	Aug 5, 2003
		- removed note_table init from ParseNewMidi, entries are now fully reset
		  in do_note_on
		- moved fast kill envelope setting to sample loading
		- removed envelopes from notes, use ones from sample instead
		- optimized do_amp_setup functions
		- do_control_volume, do_control_expression, do_channel_pressure,
		  changed from setting note_table[] to note[] for performance.
		- optimizations of sample conversion,

	Aug 6, 2003
		- removed changing sample volumes, the amp setting is now apart of
		  the midi volume maths.
		- re-write conversion functions to make them more clearer and less bug prone

	Aug 7, 2003
		- fixed volume, expression and preasure changes effecting all notes.

	Aug 8, 2003
		- spead up midi processing by using an event index

	Aug 9, 2003
		- spead up sampling and mixing by using a seperate function depending on fixed
		  modes
		- fixed data lock where it would sleep reguardless of lock state
		- fixed memory leak ... oops, forgot a free
		- removed track data storge, isn't required by the core functions

	Aug 10, 2003
		- created error function and changed all error messages to use it
		- malloc, calloc, realloc audit ensuring all are error checked.
		- fixed potential reading beyond end of midi bug

	Aug 11, 2003
		- fixed expensive interpolation over-running the sample buffer
		- changed stereo option so that changing it worked right away

	Aug 14, 2003
		- optimizations for and to the frequency calc code
		- removal of wide stereo (it sucked anyway)

	Aug 15, 2003
		- fixed volume levels for panning when non-linear volumes are used
		- removed fake reberb, it sucked on better sound systems.

	Aug 17, 2003
		- fixed autoamp
		- added env_time#= and env_level#= to timiidty.cfg parser.
		- fixed bug where last event in the midi before the last eot would
		  have a large delta. This is a bug in the midi file itself.
		- fixed some midi's having no sound cause they don't supply patch information.
		  Now defaulting all channels to bank 0, patch 0.

	Aug 18, 2003
		- preload samples
		- optimized envelope checking and controler code
		- fixed bug where some samples have an envelope rate setting that doesn't
		  actually mean anything .. ie: 0x00 0x40 0x80 0xC0
		- fixed amp bug where initial left/right levels were set to 0
		- added timidity's keep=[loop|env] support for drum patches,
		  now ignores loops and env settings for drum patches unless keep=[loop|env]
		  is in the patch line in the config.

	Aug 21, 2003
		- float to fixed point math conversions.
		- frequency range locked to 100 steps between notes
		  NOTE:need to test slow pitchbends with this, fast ones are fine

	Aug 24, 2003
		- optimized sample conversions
		- optimized note handling and sample/envelope position checks

	Aug 28, 2003
		- compile level optimizations
		  NOTE: gcc builtins used
		- numerous bug fixes

	Aug 30, 2003
		- fixed sample inc calculation bug
		- fixed panning volumes, now percieved volume of the sample is the same no matter
		  panning position.

`	Sep 2, 2003
		- made noteoff/hold behaviour match midi standard
		- added remove=sustain to patch line
		- added all sound off controller
		- fixed all notes off to only effect notes that aren't being held
		- changed envelope behaviour so that only non-sustaned samples hit envelope 4
		  while note is on.
		- Added all controllers off event


	Sep 4, 2003
		- added master sample data lock
		- improved performance of the resampling algo

	========================================
*/

#include <kos.h>
#include <kos/fs.h>

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#ifndef _WIN32
#include <pwd.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef _WIN32
# include <windows.h>
#endif
#include "config.h"
#include "wildmidi_lib.h"

/*
 * =========================
 * Global Data and Data Structs
 * =========================
 */

int WM_Initialized = 0;
signed short int WM_MasterVolume = 948;
unsigned short int WM_SampleRate = 0;
unsigned short int WM_MixerOptions = 0;

char WM_Version[] = "WildMidi Processing Library " WILDMIDILIB_VERSION;

struct _lowpass {
	signed long int in[2];
	signed long int out[2];
};

struct _filter {
	signed long int *delay[4][2];
	unsigned long int delay_pos[4][2];
	struct _lowpass lowpass[4][2];
	signed long int in[2][2];
	signed long int out[2][2];
};

struct _env {
	float time;
	float level;
	unsigned char set;
};

struct _sample {
	unsigned long int data_length;
	unsigned long int loop_start;
	unsigned long int loop_end;
	unsigned long int loop_size;
	unsigned char loop_fraction;
	unsigned short int rate;
	unsigned long int freq_low;
	unsigned long int freq_high;
	unsigned long int freq_root;
	unsigned char modes;
	unsigned long int env_rate[7];
	unsigned long int env_target[7];
	unsigned long int inc_div;
	signed short *data;
	signed short max_peek;
	signed short min_peek;
	signed long int peek_adjust;
	struct _sample *next;
};

struct _patch {
	unsigned short patchid;
	unsigned char loaded;
	char *filename;
	signed short int amp;
	unsigned char keep;
	unsigned char remove;
	struct _env env[6];
	unsigned char note;
	unsigned long int inuse_count;
	struct _sample *first_sample;
	struct _patch *next;
};

struct _patch *patch[128];

int patch_lock;

struct _channel {
	unsigned char bank;
	struct _patch *patch;
	unsigned char hold;
	unsigned char volume;
	unsigned char pressure;
	unsigned char expression;
	signed char balance;
	signed char pan;
	signed short int left_adjust;
	signed short int right_adjust;
	signed short int pitch;
	signed short int pitch_range;
	signed long int pitch_adjust;
	unsigned short reg_data;
};

#define HOLD_OFF 0x02

struct _note {
	unsigned short noteid;
	unsigned char velocity;
	struct _patch *patch;
	struct _sample *sample;
	unsigned long int sample_pos;
	unsigned long int sample_inc;
	signed long int env_inc;
	unsigned char env;
	unsigned long int env_level;
	unsigned char modes;
	unsigned char hold;
	unsigned char active;
	struct _note *next;
	signed short int vol_lvl;
};

struct _miditrack {
	unsigned long int length;
	unsigned long int ptr;
	unsigned long int delta;
	unsigned char running_event;
	unsigned char EOT;
};

struct _mdi_patches {
	struct _patch *patch;
	struct _mdi_patch *next;
};

struct _mdi_index {
	unsigned long int offset;
	unsigned char event;
	unsigned long int delta;
};

struct _mdi {
	int lock;
	unsigned char *data;
	unsigned long int size;
	unsigned short int divisions ;
	unsigned short midi_master_vol;
	unsigned long int samples_per_delta;
	unsigned long int samples_to_mix;
	struct _mdi_index * index;
	unsigned long int index_count;
	unsigned long int index_size;
	struct _WM_Info info;
	struct _WM_Info *tmp_info;
	unsigned char recalc_samples;
	struct _channel channel[16];
	struct _note *note[128];
	struct _note **last_note;
	struct _note note_table[2][16][128];

	struct _patch **patches;
	unsigned long int patch_count;
	unsigned long int sample_count;
	signed short int amp;

// setup data for auto amp
	signed long int log_cur_vol;
	signed long int lin_cur_vol;
	signed long int log_max_vol;
	signed long int lin_max_vol;

	unsigned char ch_vol[16];
	unsigned char ch_exp[16];
	unsigned char note_vel[16][128];

	struct _filter filter;
};

/* Gauss Interpolation code adapted from code supplied by Eric. A. Welsh */

double newt_coeffs[58][58];		/* for start/end of samples */
float *gauss_table[(1<<10)] = {0};	/* don't need doubles */
int gauss_window[35] = {0};
int gauss_n = 34;	/* do not set this value higher than 34 */
			/* 34 is as high as we can go before errors crop up */

void init_gauss (void) {
	/* init gauss table */
	int n = 34;
	int m, i, k, n_half = (n>>1);
	int j;
	int sign;
	double ck;
	double x, x_inc, xz;
	double z[35];
	float *gptr;

	newt_coeffs[0][0] = 1;

	for (i = 0; i <= n; i++) {
		newt_coeffs[i][0] = 1;
		newt_coeffs[i][i] = 1;

		if (i > 1) {
			newt_coeffs[i][0] = newt_coeffs[i-1][0] / i;
			newt_coeffs[i][i] = newt_coeffs[i-1][0] / i;
		}

		for (j = 1; j < i; j++) {
			newt_coeffs[i][j] = newt_coeffs[i-1][j-1] + newt_coeffs[i-1][j];
			if (i > 1)
				newt_coeffs[i][j] /= i;
		}
		z[i] = i / (4*M_PI);
	}

	for (i = 0; i <= n; i++)
		for (j = 0, sign = pow(-1, i); j <= i; j++, sign *= -1)
			newt_coeffs[i][j] *= sign;


	x_inc = 1.0 / (1<<10);
	for (m = 0, x = 0.0; m < (1<<10); m++, x += x_inc) {
		xz = (x + n_half) / (4*M_PI);
		gptr = gauss_table[m] = realloc(gauss_table[m], (n+1)*sizeof(float));

		for (k = 0; k <= n; k++) {
			ck = 1.0;

			for (i = 0; i <= n; i++) {
				if (i == k)
					continue;

				ck *= (sin(xz - z[i])) / (sin(z[k] - z[i]));
    			}
			*gptr++ = ck;
		}
	}
}

unsigned long int delay_size[4][2];
signed long int a[5][2];
signed long int b[5][2];
signed long int gain_in[4];
signed long int gain_out[4];

void init_lowpass (void) {
	float c = 0;
	int i;
	float f[] = { 512.0, 1024.0, 2048.0, 4096.0 , 8192.0};
	float aa, ab, ba, bb;

	for (i = 0; i < 5; i++) {
		c = 1.0 / tan(3.141592654 * f[i] / WM_SampleRate);
		aa = 1.0 / (1.0 + 1.4 * c + c * c);
		ab = 2.0 * aa;
		ba = 2.0 * (1.0 - c * c) * aa;
		bb = (1.0 - 1.4 * c + c * c) * aa;
		a[i][0] = (signed long int)(aa * 1024.0);
		a[i][1] = (signed long int)(ab * 1024.0);
		b[i][0] = (signed long int)(ba * 1024.0);
		b[i][1] = (signed long int)(bb * 1024.0);
	}
	gain_in[0] = 772;
	gain_out[0] = 772;
	gain_in[1] = 570;
	gain_out[1] = 570;
	gain_in[2] = 520;
	gain_out[2] = 520;
	gain_in[3] = 512;
	gain_out[3] = 512;

	delay_size[0][0] = 2191 * WM_SampleRate / 44100;
	delay_size[0][1] = (2191 + 19) * WM_SampleRate / 44100;
	delay_size[1][0] = (2971 + 19) * WM_SampleRate / 44100;
	delay_size[1][1] = 2971 * WM_SampleRate / 44100;
	delay_size[2][0] = 3253 * WM_SampleRate / 44100;
	delay_size[2][1] = (3253 + 19) * WM_SampleRate / 44100;
	delay_size[3][0] = (3307 + 19) * WM_SampleRate / 44100;
	delay_size[3][1] = 3307 * WM_SampleRate / 44100;

}

struct _hndl {
	void * handle;
	struct _hndl *next;
	struct _hndl *prev;
};

struct _hndl * first_handle = NULL;

//f: ( VOLUME / 127 )
//f: pow(( VOLUME / 127 ), 1.660964047 )
//f: pow(( VOLUME / 127 ), 2.0 )
//f: pow(( VOLUME / 127 ), 0.602059991 )
//f: pow(( VOLUME / 127 ), 0.5 )

signed short int lin_volume[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 129, 137, 145, 153, 161, 169, 177, 185, 193, 201, 209, 217, 225, 233, 241, 249, 258, 266, 274, 282, 290, 298, 306, 314, 322, 330, 338, 346, 354, 362, 370, 378, 387, 395, 403, 411, 419, 427, 435, 443, 451, 459, 467, 475, 483, 491, 499, 507, 516, 524, 532, 540, 548, 556, 564, 572, 580, 588, 596, 604, 612, 620, 628, 636, 645, 653, 661, 669, 677, 685, 693, 701, 709, 717, 725, 733, 741, 749, 757, 765, 774, 782, 790, 798, 806, 814, 822, 830, 838, 846, 854, 862, 870, 878, 886, 894, 903, 911, 919, 927, 935, 943, 951, 959, 967, 975, 983, 991, 999, 1007, 1015, 1024 };
signed short int log_volume[] = { 0, 0, 1, 2, 3, 4, 6, 8, 10, 12, 15, 17, 20, 23, 26, 29, 32, 36, 39, 43, 47, 51, 55, 59, 64, 68, 73, 78, 83, 88, 93, 98, 103, 109, 114, 120, 126, 132, 138, 144, 150, 156, 162, 169, 176, 182, 189, 196, 203, 210, 217, 224, 232, 239, 247, 255, 262, 270, 278, 286, 294, 302, 311, 319, 328, 336, 345, 353, 362, 371, 380, 389, 398, 408, 417, 426, 436, 446, 455, 465, 475, 485, 495, 505, 515, 525, 535, 546, 556, 567, 577, 588, 599, 610, 621, 632, 643, 654, 665, 677, 688, 699, 711, 723, 734, 746, 758, 770, 782, 794, 806, 818, 831, 843, 855, 868, 880, 893, 906, 919, 931, 944, 957, 970, 984, 997, 1010, 1024 };
signed short int sqr_volume[] = { 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 9, 10, 12, 14, 16, 18, 20, 22, 25, 27, 30, 33, 36, 39, 42, 46, 49, 53, 57, 61, 65, 69, 73, 77, 82, 86, 91, 96, 101, 106, 111, 117, 122, 128, 134, 140, 146, 152, 158, 165, 171, 178, 185, 192, 199, 206, 213, 221, 228, 236, 244, 251, 260, 268, 276, 284, 293, 302, 311, 320, 329, 338, 347, 357, 366, 376, 386, 396, 406, 416, 426, 437, 447, 458, 469, 480, 491, 502, 514, 525, 537, 549, 560, 572, 585, 597, 609, 622, 634, 647, 660, 673, 686, 699, 713, 726, 740, 754, 768, 782, 796, 810, 825, 839, 854, 869, 884, 899, 914, 929, 944, 960, 976, 992, 1007, 1024 };
//signed short int pan_volume[] = { 0, 55, 84, 107, 127, 146, 162, 178, 193, 208, 221, 234, 247, 259, 271, 282, 294, 305, 315, 326, 336, 346, 356, 366, 375, 384, 394, 403, 412, 420, 429, 438, 446, 454, 463, 471, 479, 487, 495, 503, 510, 518, 525, 533, 540, 548, 555, 562, 570, 577, 584, 591, 598, 605, 611, 618, 625, 632, 638, 645, 651, 658, 664, 671, 677, 684, 690, 696, 703, 709, 715, 721, 727, 733, 739, 745, 751, 757, 763, 769, 775, 781, 786, 792, 798, 804, 809, 815, 821, 826, 832, 837, 843, 848, 854, 859, 865, 870, 876, 881, 886, 892, 897, 902, 907, 913, 918, 923, 928, 933, 939, 944, 949, 954, 959, 964, 969, 974, 979, 984, 989, 994, 999, 1004, 1009, 1014, 1019, 1024 };
signed short int pan_volume[] = { 0, 90, 128, 157, 181, 203, 222, 240, 257, 272, 287, 301, 314, 327, 339, 351, 363, 374, 385, 396, 406, 416, 426, 435, 445, 454, 463, 472, 480, 489, 497, 505, 514, 521, 529, 537, 545, 552, 560, 567, 574, 581, 588, 595, 602, 609, 616, 622, 629, 636, 642, 648, 655, 661, 667, 673, 679, 686, 692, 697, 703, 709, 715, 721, 726, 732, 738, 743, 749, 754, 760, 765, 771, 776, 781, 786, 792, 797, 802, 807, 812, 817, 822, 827, 832, 837, 842, 847, 852, 857, 862, 866, 871, 876, 880, 885, 890, 894, 899, 904, 908, 913, 917, 922, 926, 931, 935, 939, 944, 948, 953, 957, 961, 965, 970, 974, 978, 982, 987, 991, 995, 999, 1003, 1007, 1011, 1015, 1019, 1024 };

unsigned long int reverb_val = 92;
unsigned long int comb_size[8][2];
unsigned long int allpass_size[2][2];

float env_time_table[] = {
	0.0, 0.092857143, 0.046428571, 0.030952381, 0.023214286, 0.018571429, 0.015476190, 0.013265306, 0.011607143, 0.010317460, 0.009285714, 0.008441558, 0.007738095, 0.007142857, 0.006632653, 0.006190476, 0.005803571, 0.005462185, 0.005158730, 0.004887218, 0.004642857, 0.004421769, 0.004220779, 0.004037267, 0.003869048, 0.003714286, 0.003571429, 0.003439153, 0.003316327, 0.003201970, 0.003095238, 0.002995392, 0.002901786, 0.002813853, 0.002731092, 0.002653061, 0.002579365, 0.002509653, 0.002443609, 0.002380952, 0.002321429, 0.002264808, 0.002210884, 0.002159468, 0.002110390, 0.002063492, 0.002018634, 0.001975684, 0.001934524, 0.001895044, 0.001857143, 0.001820728, 0.001785714, 0.001752022, 0.001719577, 0.001688312, 0.001658163, 0.001629073, 0.001600985, 0.001573850, 0.001547619, 0.001522248, 0.001497696, 0.001473923,
	0.0, 0.742857143, 0.371428571, 0.247619048, 0.185714286, 0.148571429, 0.123809524, 0.106122449, 0.092857143, 0.082539683, 0.074285714, 0.067532468, 0.061904762, 0.057142857, 0.053061224, 0.049523810, 0.046428571, 0.043697479, 0.041269841, 0.039097744, 0.037142857, 0.035374150, 0.033766234, 0.032298137, 0.030952381, 0.029714286, 0.028571429, 0.027513228, 0.026530612, 0.025615764, 0.024761905, 0.023963134, 0.023214286, 0.022510823, 0.021848739, 0.021224490, 0.020634921, 0.020077220, 0.019548872, 0.019047619, 0.018571429, 0.018118467, 0.017687075, 0.017275748, 0.016883117, 0.016507937, 0.016149068, 0.015805471, 0.015476190, 0.015160350, 0.014857143, 0.014565826, 0.014285714, 0.014016173, 0.013756614, 0.013506494, 0.013265306, 0.013032581, 0.012807882, 0.012590799, 0.012380952, 0.012177986, 0.011981567, 0.011791383,
	0.0, 5.942857143, 2.971428571, 1.980952381, 1.485714286, 1.188571429, 0.990476190, 0.848979592, 0.742857143, 0.660317460, 0.594285714, 0.540259740, 0.495238095, 0.457142857, 0.424489796, 0.396190476, 0.371428571, 0.349579832, 0.330158730, 0.312781955, 0.297142857, 0.282993197, 0.270129870, 0.258385093, 0.247619048, 0.237714286, 0.228571429, 0.220105820, 0.212244898, 0.204926108, 0.198095238, 0.191705069, 0.185714286, 0.180086580, 0.174789916, 0.169795918, 0.165079365, 0.160617761, 0.156390977, 0.152380952, 0.148571429, 0.144947735, 0.141496599, 0.138205980, 0.135064935, 0.132063492, 0.129192547, 0.126443769, 0.123809524, 0.121282799, 0.118857143, 0.116526611, 0.114285714, 0.112129380, 0.110052910, 0.108051948, 0.106122449, 0.104260652, 0.102463054, 0.100726392, 0.099047619, 0.097423888, 0.095852535, 0.094331066,
	0.0, 47.542857143, 23.771428571, 15.847619048, 11.885714286, 9.508571429, 7.923809524, 6.791836735, 5.942857143, 5.282539683, 4.754285714, 4.322077922, 3.961904762, 3.657142857, 3.395918367, 3.169523810, 2.971428571, 2.796638655, 2.641269841, 2.502255639, 2.377142857, 2.263945578, 2.161038961, 2.067080745, 1.980952381, 1.901714286, 1.828571429, 1.760846561, 1.697959184, 1.639408867, 1.584761905, 1.533640553, 1.485714286, 1.440692641, 1.398319328, 1.358367347, 1.320634921, 1.284942085, 1.251127820, 1.219047619, 1.188571429, 1.159581882, 1.131972789, 1.105647841, 1.080519481, 1.056507937, 1.033540373, 1.011550152, 0.990476190, 0.970262391, 0.950857143, 0.932212885, 0.914285714, 0.897035040, 0.880423280, 0.864415584, 0.848979592, 0.834085213, 0.819704433, 0.805811138, 0.792380952, 0.779391101, 0.766820276, 0.754648526
};

#if 0
unsigned long int freq_table[] = {
	8372018, 8376855, 8381695, 8386538, 8391384, 8396232, 8401084, 8405938, 8410795, 8415654, 8420517, 8425382, 8430250, 8435121, 8439995, 8444871, 8449751, 8454633, 8459518, 8464406, 8469296, 8474190, 8479086, 8483985, 8488887, 8493792, 8498700, 8503610, 8508523, 8513439, 8518358, 8523280, 8528205, 8533132, 8538063, 8542996, 8547932, 8552871, 8557813, 8562757, 8567705, 8572655, 8577608, 8582564, 8587523, 8592485, 8597450, 8602417, 8607387, 8612361, 8617337, 8622316, 8627298, 8632282, 8637270, 8642261, 8647254, 8652250, 8657250, 8662252, 8667257, 8672264, 8677275, 8682289, 8687305, 8692325, 8697347, 8702372, 8707400, 8712431, 8717465, 8722502, 8727542, 8732585, 8737630, 8742679, 8747730, 8752785, 8757842, 8762902, 8767965, 8773031, 8778100, 8783172, 8788247, 8793325, 8798405, 8803489, 8808575, 8813665, 8818757, 8823853, 8828951, 8834052, 8839157, 8844264, 8849374, 8854487, 8859603, 8864722, 8869844, 8874969, 8880097, 8885227, 8890361, 8895498, 8900638, 8905780, 8910926, 8916075, 8921226, 8926381, 8931538, 8936699, 8941863, 8947029, 8952199, 8957371, 8962546, 8967725, 8972906, 8978091, 8983278, 8988469, 8993662, 8998859, 9004058, 9009260, 9014466, 9019674, 9024886, 9030100, 9035318, 9040538, 9045762, 9050988, 9056218, 9061451, 9066686, 9071925, 9077166, 9082411, 9087659, 9092910, 9098163, 9103420, 9108680, 9113943, 9119209, 9124478, 9129750, 9135025, 9140303, 9145584, 9150868, 9156156, 9161446, 9166739, 9172036, 9177335, 9182638, 9187944, 9193252, 9198564, 9203879, 9209197, 9214518, 9219842, 9225169, 9230499, 9235832, 9241169, 9246508, 9251851, 9257196, 9262545, 9267897, 9273252, 9278610, 9283971, 9289335, 9294702, 9300073, 9305446, 9310823, 9316202, 9321585, 9326971, 9332360, 9337752, 9343147, 9348546, 9353947, 9359352, 9364760, 9370171, 9375585, 9381002, 9386422, 9391845, 9397272, 9402701, 9408134, 9413570, 9419009, 9424451, 9429897, 9435345, 9440797, 9446252, 9451710, 9457171, 9462635, 9468102, 9473573, 9479047, 9484524, 9490004, 9495487, 9500973, 9506463, 9511955, 9517451, 9522950, 9528453, 9533958, 9539467, 9544979, 9550494, 9556012, 9561533, 9567058, 9572585, 9578116, 9583650, 9589188, 9594728, 9600272, 9605819, 9611369, 9616922, 9622479, 9628039, 9633602, 9639168, 9644737, 9650310, 9655886, 9661465, 9667047, 9672633, 9678221, 9683813, 9689409, 9695007, 9700609, 9706214, 9711822, 9717433, 9723048, 9728666, 9734287, 9739911, 9745539, 9751170, 9756804, 9762441, 9768082, 9773726, 9779373, 9785023, 9790677, 9796334, 9801994, 9807657, 9813324, 9818994, 9824667, 9830344, 9836024, 9841707, 9847394, 9853083, 9858776, 9864473, 9870172, 9875875, 9881581, 9887291, 9893003, 9898719, 9904439, 9910162, 9915887, 9921617, 9927349, 9933085, 9938825, 9944567, 9950313, 9956062, 9961815, 9967570, 9973330, 9979092, 9984858, 9990627, 9996399, 10002175, 10007954, 10013737, 10019523, 10025312, 10031104, 10036900, 10042700, 10048502, 10054308, 10060117, 10065930, 10071746, 10077565, 10083388, 10089214, 10095043, 10100876, 10106712, 10112552, 10118395, 10124241, 10130091, 10135944, 10141800, 10147660, 10153523, 10159390, 10165260, 10171133, 10177010, 10182890, 10188774, 10194661, 10200551, 10206445, 10212342, 10218243, 10224147, 10230054, 10235965, 10241879, 10247797, 10253718, 10259642, 10265570, 10271502, 10277436, 10283374, 10289316, 10295261, 10301210, 10307162, 10313117, 10319076, 10325038, 10331004, 10336973, 10342945, 10348921, 10354901, 10360884, 10366870, 10372860, 10378853, 10384850, 10390850, 10396854, 10402861, 10408872, 10414886, 10420904, 10426925, 10432949, 10438977, 10445009, 10451044, 10457083, 10463124, 10469170, 10475219, 10481271, 10487327, 10493387, 10499450, 10505516, 10511586, 10517660, 10523737, 10529817, 10535901, 10541989, 10548080, 10554174, 10560273, 10566374, 10572480, 10578589, 10584701, 10590817, 10596936, 10603059, 10609186, 10615316, 10621449, 10627586, 10633727, 10639871, 10646019, 10652170, 10658325, 10664483, 10670645, 10676811, 10682980, 10689153, 10695329, 10701509, 10707692, 10713879, 10720069, 10726264, 10732461, 10738662, 10744867, 10751076, 10757288, 10763503, 10769722, 10775945, 10782172, 10788402, 10794635, 10800872, 10807113, 10813357, 10819605, 10825857, 10832112, 10838371, 10844634, 10850900, 10857169, 10863443, 10869720, 10876000, 10882284, 10888572, 10894864, 10901159, 10907457, 10913760, 10920066, 10926375, 10932689, 10939006, 10945326, 10951650, 10957978, 10964310, 10970645, 10976984, 10983326, 10989673, 10996022, 11002376, 11008733, 11015094, 11021459, 11027827, 11034199, 11040574, 11046954, 11053337, 11059723, 11066114, 11072508, 11078905, 11085307, 11091712, 11098121, 11104533, 11110949, 11117369, 11123793, 11130220, 11136651, 11143086, 11149525, 11155967, 11162413, 11168863, 11175316, 11181773, 11188234, 11194699, 11201167, 11207639, 11214115, 11220594, 11227078, 11233565, 11240055, 11246550, 11253048, 11259550, 11266056, 11272566, 11279079, 11285596, 11292117, 11298642, 11305170, 11311702, 11318238, 11324778, 11331321, 11337868, 11344420, 11350974, 11357533, 11364095, 11370662, 11377232, 11383805, 11390383, 11396964, 11403550, 11410139, 11416731, 11423328, 11429928, 11436533, 11443141, 11449753, 11456368, 11462988, 11469611, 11476238, 11482869, 11489504, 11496143, 11502785, 11509432, 11516082, 11522736, 11529394, 11536056, 11542721, 11549390, 11556064, 11562741, 11569422, 11576107, 11582795, 11589488, 11596184, 11602885, 11609589, 11616297, 11623009, 11629725, 11636444, 11643168, 11649895, 11656627, 11663362, 11670101, 11676844, 11683591, 11690342, 11697097, 11703855, 11710618, 11717384, 11724154, 11730929, 11737707, 11744489, 11751275, 11758065, 11764859, 11771656, 11778458, 11785264, 11792073, 11798887, 11805704, 11812526, 11819351, 11826180, 11833013, 11839851, 11846692, 11853537, 11860386, 11867239, 11874096, 11880956, 11887821, 11894690, 11901563, 11908440, 11915320, 11922205, 11929094, 11935986, 11942883, 11949784, 11956688, 11963597, 11970510, 11977426, 11984347, 11991271, 11998200, 12005133, 12012069, 12019010, 12025954, 12032903, 12039856, 12046812, 12053773, 12060738, 12067706, 12074679, 12081656, 12088637, 12095622, 12102610, 12109603, 12116600, 12123601, 12130606, 12137615, 12144629, 12151646, 12158667, 12165692, 12172722, 12179755, 12186793, 12193834, 12200880, 12207930, 12214983, 12222041, 12229103, 12236169, 12243239, 12250313, 12257392, 12264474, 12271561, 12278651, 12285746, 12292844, 12299947, 12307054, 12314165, 12321280, 12328400, 12335523, 12342651, 12349782, 12356918, 12364058, 12371202, 12378350, 12385502, 12392658, 12399819, 12406984, 12414152, 12421325, 12428502, 12435684, 12442869, 12450059, 12457252, 12464450, 12471652, 12478858, 12486069, 12493283, 12500502, 12507725, 12514952, 12522183, 12529418, 12536658, 12543901, 12551149, 12558401, 12565658, 12572918, 12580183, 12587452, 12594725, 12602002, 12609283, 12616569, 12623859, 12631153, 12638451, 12645754, 12653061, 12660372, 12667687, 12675006, 12682330, 12689658, 12696990, 12704326, 12711667, 12719012, 12726361, 12733714, 12741072, 12748433, 12755800, 12763170, 12770544, 12777923, 12785306, 12792694, 12800085, 12807481, 12814882, 12822286, 12829695, 12837108, 12844525, 12851947, 12859373, 12866803, 12874237, 12881676, 12889119, 12896567, 12904018, 12911474, 12918934, 12926399, 12933868, 12941341, 12948819, 12956301, 12963787, 12971277, 12978772, 12986271, 12993775, 13001283, 13008795, 13016311, 13023832, 13031357, 13038887, 13046421, 13053959, 13061502, 13069049, 13076600, 13084156, 13091716, 13099280, 13106849, 13114422, 13122000, 13129582, 13137168, 13144759, 13152354, 13159953, 13167557, 13175165, 13182778, 13190395, 13198016, 13205642, 13213273, 13220907, 13228546, 13236190, 13243838, 13251490, 13259147, 13266808, 13274474, 13282144, 13289818, 13297497, 13305180, 13312868, 13320560, 13328257, 13335958, 13343663, 13351373, 13359088, 13366807, 13374530, 13382258, 13389990, 13397727, 13405468, 13413214, 13420964, 13428719, 13436478, 13444241, 13452010, 13459782, 13467559, 13475341, 13483127, 13490918, 13498713, 13506512, 13514316, 13522125, 13529938, 13537756, 13545578, 13553405, 13561236, 13569071, 13576912, 13584756, 13592606, 13600460, 13608318, 13616181, 13624048, 13631920, 13639797, 13647678, 13655564, 13663454, 13671349, 13679248, 13687152, 13695060, 13702974, 13710891, 13718813, 13726740, 13734671, 13742607, 13750548, 13758493, 13766443, 13774397, 13782356, 13790319, 13798287, 13806260, 13814237, 13822219, 13830206, 13838197, 13846193, 13854193, 13862198, 13870208, 13878222, 13886241, 13894264, 13902292, 13910325, 13918363, 13926405, 13934451, 13942503, 13950559, 13958619, 13966685, 13974755, 13982829, 13990909, 13998993, 14007081, 14015175, 14023273, 14031375, 14039483, 14047595, 14055712, 14063833, 14071959, 14080090, 14088225, 14096366, 14104511, 14112660, 14120815, 14128974, 14137137, 14145306, 14153479, 14161657, 14169840, 14178027, 14186219, 14194416, 14202618, 14210824, 14219035, 14227251, 14235471, 14243697, 14251927, 14260161, 14268401, 14276645, 14284894, 14293148, 14301407, 14309670, 14317938, 14326211, 14334489, 14342772, 14351059, 14359351, 14367648, 14375949, 14384256, 14392567, 14400883, 14409204, 14417530, 14425860, 14434196, 14442536, 14450881, 14459230, 14467585, 14475944, 14484309, 14492678, 14501052, 14509430, 14517814, 14526202, 14534596, 14542994, 14551397, 14559805, 14568217, 14576635, 14585057, 14593485, 14601917, 14610354, 14618796, 14627242, 14635694, 14644151, 14652612, 14661078, 14669550, 14678026, 14686507, 14694993, 14703483, 14711979, 14720480, 14728985, 14737496, 14746011, 14754531, 14763057, 14771587, 14780122, 14788662, 14797207, 14805757, 14814311, 14822871, 14831436, 14840005, 14848580, 14857160, 14865744, 14874334, 14882928, 14891527, 14900132, 14908741, 14917355, 14925975, 14934599, 14943228, 14951862, 14960502, 14969146, 14977795, 14986449, 14995109, 15003773, 15012442, 15021116, 15029795, 15038480, 15047169, 15055863, 15064563, 15073267, 15081976, 15090691, 15099410, 15108135, 15116864, 15125599, 15134338, 15143083, 15151833, 15160587, 15169347, 15178112, 15186882, 15195657, 15204437, 15213222, 15222013, 15230808, 15239608, 15248414, 15257224, 15266040, 15274861, 15283687, 15292518, 15301354, 15310195, 15319041, 15327893, 15336749, 15345611, 15354477, 15363349, 15372226, 15381108, 15389996, 15398888, 15407786, 15416688, 15425596, 15434509, 15443427, 15452350, 15461279, 15470212, 15479151, 15488095, 15497044, 15505998, 15514958, 15523922, 15532892, 15541867, 15550847, 15559832, 15568823, 15577819, 15586820, 15595826, 15604837, 15613853, 15622875, 15631902, 15640934, 15649972, 15659014, 15668062, 15677115, 15686173, 15695237, 15704306, 15713380, 15722459, 15731543, 15740633, 15749728, 15758828, 15767934, 15777045, 15786161, 15795282, 15804408, 15813540, 15822677, 15831820, 15840967, 15850120, 15859279, 15868442, 15877611, 15886785, 15895965, 15905149, 15914339, 15923535, 15932735, 15941941, 15951153, 15960369, 15969591, 15978818, 15988051, 15997289, 16006532, 16015781, 16025035, 16034294, 16043559, 16052829, 16062104, 16071385, 16080671, 16089962, 16099259, 16108561, 16117869, 16127182, 16136500, 16145824, 16155153, 16164488, 16173828, 16183173, 16192523, 16201880, 16211241, 16220608, 16229980, 16239358, 16248741, 16258130, 16267524, 16276923, 16286328, 16295738, 16305154, 16314575, 16324002, 16333434, 16342871, 16352314, 16361763, 16371217, 16380676, 16390141, 16399611, 16409087, 16418568, 16428055, 16437547, 16447044, 16456548, 16466056, 16475570, 16485090, 16494615, 16504146, 16513682, 16523224, 16532771, 16542323, 16551882, 16561445, 16571015, 16580589, 16590170, 16599755, 16609347, 16618944, 16628546, 16638154, 16647768, 16657387, 16667012, 16676642, 16686278, 16695919, 16705566, 16715219, 16724877, 16734540
};
#endif

unsigned long int freq_table[] = {
	837201792, 837685632, 838169728, 838653568, 839138240, 839623232, 840108480, 840593984, 841079680, 841565184, 842051648, 842538240, 843025152, 843512320, 843999232, 844486976, 844975040, 845463360, 845951936, 846440320, 846929536, 847418944, 847908608, 848398656, 848888960, 849378944, 849869824, 850361024, 850852416, 851344192, 851835584, 852327872, 852820480, 853313280, 853806464, 854299328, 854793024, 855287040, 855781312, 856275904, 856770752, 857265344, 857760704, 858256448, 858752448, 859248704, 859744768, 860241600, 860738752, 861236160, 861733888, 862231360, 862729600, 863228160, 863727104, 864226176, 864725696, 865224896, 865724864, 866225152, 866725760, 867226688, 867727296, 868228736, 868730496, 869232576, 869734912, 870236928, 870739904, 871243072, 871746560, 872250368, 872754496, 873258240, 873762880, 874267840, 874773184, 875278720, 875783936, 876290112, 876796480, 877303232, 877810176, 878317504, 878824512, 879332416, 879840576, 880349056, 880857792, 881366272, 881875712, 882385280, 882895296, 883405440, 883915456, 884426304, 884937408, 885448832, 885960512, 886472512,
	886984192, 887496768, 888009728, 888522944, 889036352, 889549632, 890063680, 890578048, 891092736, 891607680, 892122368, 892637952, 893153792, 893670016, 894186496, 894703232, 895219648, 895737024, 896254720, 896772672, 897290880, 897808896, 898327744, 898846912, 899366336, 899886144, 900405568, 900925952, 901446592, 901967552, 902488768, 903010368, 903531584, 904053760, 904576256, 905099008, 905622016, 906144896, 906668480, 907192512, 907716800, 908241408, 908765632, 909290816, 909816256, 910342144, 910868160, 911394624, 911920768, 912447680, 912975104, 913502720, 914030592, 914558208, 915086784, 915615552, 916144768, 916674176, 917203968, 917733440, 918263744, 918794496, 919325440, 919856704, 920387712, 920919616, 921451840, 921984320, 922517184, 923049728, 923583168, 924116928, 924651008, 925185344, 925720000, 926254336, 926789696, 927325312, 927861120, 928397440, 928933376, 929470208, 930007296, 930544768, 931082560, 931619968, 932158464, 932697152, 933236160, 933775488, 934315072, 934854464, 935394688, 935935296, 936476224, 937017344, 937558208, 938100160, 938642304, 939184640,
	939727488, 940269888, 940813312, 941357056, 941900992, 942445440, 942990016, 943534400, 944079680, 944625280, 945171200, 945717440, 946263360, 946810176, 947357376, 947904832, 948452672, 949000192, 949548608, 950097280, 950646400, 951195776, 951745472, 952294912, 952845184, 953395904, 953946880, 954498176, 955049216, 955601088, 956153408, 956705920, 957258816, 957812032, 958364928, 958918848, 959472960, 960027456, 960582272, 961136768, 961692224, 962248000, 962804032, 963360448, 963916608, 964473600, 965031040, 965588736, 966146816, 966705152, 967263168, 967822144, 968381440, 968941120, 969501056, 970060736, 970621376, 971182272, 971743488, 972305088, 972866368, 973428608, 973991104, 974554048, 975117312, 975680768, 976243968, 976808192, 977372736, 977937536, 978502656, 979067584, 979633344, 980199488, 980765888, 981332736, 981899200, 982466688, 983034432, 983602624, 984171008, 984739776, 985308160, 985877632, 986447360, 987017472, 987587904, 988157952, 988729088, 989300416, 989872192, 990444224, 991016000, 991588672, 992161728, 992735168, 993308864, 993882880, 994456576, 995031296,
	995606336, 996181696, 996757440, 997332800, 997909184, 998485888, 999062912, 999640256, 1000217984, 1000795392, 1001373696, 1001952448, 1002531520, 1003110848, 1003689920, 1004270016, 1004850304, 1005431040, 1006012160, 1006592832, 1007174592, 1007756608, 1008339008, 1008921792, 1009504768, 1010087552, 1010671296, 1011255360, 1011839808, 1012424576, 1013009024, 1013594368, 1014180160, 1014766272, 1015352768, 1015938880, 1016526016, 1017113472, 1017701248, 1018289408, 1018877824, 1019465984, 1020055104, 1020644672, 1021234496, 1021824768, 1022414528, 1023005440, 1023596608, 1024188160, 1024780096, 1025371584, 1025964160, 1026557120, 1027150336, 1027744000, 1028337920, 1028931520, 1029526144, 1030121152, 1030716480, 1031312128, 1031907456, 1032503808, 1033100480, 1033697536, 1034294912, 1034892032, 1035490048, 1036088512, 1036687232, 1037286336, 1037885824, 1038484928, 1039085056, 1039685632, 1040286464, 1040887680, 1041488448, 1042090368, 1042692608, 1043295168, 1043898176, 1044501440, 1045104384, 1045708288, 1046312640, 1046917376, 1047522368, 1048127040, 1048732800, 1049338816, 1049945280, 1050552128, 1051158528, 1051765952, 1052373824, 1052982016, 1053590592, 1054199424,
	1054807936, 1055417600, 1056027456, 1056637760, 1057248448, 1057858752, 1058470016, 1059081728, 1059693824, 1060306304, 1060918336, 1061531392, 1062144896, 1062758656, 1063372928, 1063987392, 1064601664, 1065216896, 1065832448, 1066448448, 1067064704, 1067680704, 1068297728, 1068915136, 1069532864, 1070150976, 1070768640, 1071387520, 1072006720, 1072626240, 1073246080, 1073866368, 1074486272, 1075107200, 1075728512, 1076350208, 1076972160, 1077593856, 1078216704, 1078839680, 1079463296, 1080087040, 1080710528, 1081335168, 1081960064, 1082585344, 1083211008, 1083836928, 1084462592, 1085089280, 1085716352, 1086343936, 1086971648, 1087599104, 1088227712, 1088856576, 1089485824, 1090115456, 1090745472, 1091375104, 1092005760, 1092636928, 1093268352, 1093900160, 1094531584, 1095164160, 1095796992, 1096430336, 1097064064, 1097697280, 1098331648, 1098966400, 1099601536, 1100237056, 1100872832, 1101508224, 1102144768, 1102781824, 1103419136, 1104056832, 1104694144, 1105332608, 1105971328, 1106610432, 1107249920, 1107889152, 1108529408, 1109170048, 1109811072, 1110452352, 1111094144, 1111735552, 1112377984, 1113020928, 1113664128, 1114307712, 1114950912, 1115595264, 1116240000, 1116885120,
	1117530624, 1118175744, 1118821888, 1119468416, 1120115456, 1120762752, 1121410432, 1122057856, 1122706176, 1123355136, 1124004224, 1124653824, 1125303040, 1125953408, 1126604160, 1127255168, 1127906560, 1128557696, 1129209984, 1129862528, 1130515456, 1131168768, 1131822592, 1132475904, 1133130368, 1133785216, 1134440448, 1135096064, 1135751296, 1136407680, 1137064448, 1137721472, 1138379008, 1139036800, 1139694336, 1140353024, 1141012096, 1141671424, 1142331264, 1142990592, 1143651200, 1144312192, 1144973440, 1145635200, 1146296448, 1146958976, 1147621760, 1148285056, 1148948608, 1149612672, 1150276224, 1150940928, 1151606144, 1152271616, 1152937600, 1153603072, 1154269824, 1154936832, 1155604352, 1156272128, 1156939648, 1157608192, 1158277248, 1158946560, 1159616384, 1160286464, 1160956288, 1161627264, 1162298624, 1162970240, 1163642368, 1164314112, 1164987008, 1165660160, 1166333824, 1167007872, 1167681536, 1168356352, 1169031552, 1169707136, 1170383104, 1171059584, 1171735552, 1172412672, 1173090304, 1173768192, 1174446592, 1175124480, 1175803648, 1176483072, 1177163008, 1177843328, 1178523264, 1179204352, 1179885824, 1180567680, 1181249920, 1181932544, 1182614912, 1183298304,
	1183982208, 1184666368, 1185351040, 1186035328, 1186720640, 1187406464, 1188092672, 1188779264, 1189466368, 1190152960, 1190840832, 1191528960, 1192217600, 1192906624, 1193595136, 1194285056, 1194975232, 1195665792, 1196356736, 1197047296, 1197739136, 1198431360, 1199123968, 1199816960, 1200510336, 1201203328, 1201897600, 1202592128, 1203287040, 1203982464, 1204677504, 1205373696, 1206070272, 1206767232, 1207464704, 1208161664, 1208859904, 1209558528, 1210257536, 1210956928, 1211656832, 1212356224, 1213056768, 1213757952, 1214459392, 1215161216, 1215862656, 1216565376, 1217268352, 1217971840, 1218675712, 1219379200, 1220083840, 1220788992, 1221494528, 1222200448, 1222906752, 1223612672, 1224319872, 1225027456, 1225735424, 1226443648, 1227151616, 1227860864, 1228570496, 1229280512, 1229990912, 1230700928, 1231412096, 1232123776, 1232835840, 1233548288, 1234261248, 1234973696, 1235687424, 1236401536, 1237116032, 1237831040, 1238545536, 1239261312, 1239977472, 1240694144, 1241411072, 1242128512, 1242845568, 1243563776, 1244282496, 1245001600, 1245721088, 1246440192, 1247160448, 1247881216, 1248602368, 1249324032, 1250045184, 1250767616, 1251490432, 1252213632, 1252937344, 1253661440,
	1254385152, 1255110016, 1255835392, 1256561152, 1257287424, 1258013184, 1258740096, 1259467648, 1260195456, 1260923648, 1261651584, 1262380800, 1263110272, 1263840256, 1264570624, 1265301504, 1266031872, 1266763520, 1267495552, 1268227968, 1268961024, 1269693440, 1270427264, 1271161472, 1271896064, 1272631168, 1273365760, 1274101632, 1274838016, 1275574784, 1276311808, 1277049472, 1277786624, 1278525056, 1279264000, 1280003328, 1280743040, 1281482368, 1282222976, 1282963968, 1283705344, 1284447232, 1285188736, 1285931392, 1286674560, 1287418240, 1288162176, 1288906624, 1289650688, 1290395904, 1291141760, 1291887872, 1292634496, 1293380608, 1294128128, 1294875904, 1295624320, 1296373120, 1297122304, 1297870976, 1298621056, 1299371520, 1300122496, 1300873856, 1301624832, 1302376960, 1303129600, 1303882752, 1304636288, 1305389312, 1306143872, 1306898688, 1307654016, 1308409600, 1309165696, 1309921536, 1310678528, 1311435904, 1312193920, 1312952192, 1313710080, 1314469248, 1315228928, 1315988992, 1316749568, 1317509632, 1318271104, 1319032960, 1319795200, 1320557952, 1321321088, 1322083840, 1322847872, 1323612416, 1324377216, 1325142656, 1325907584, 1326673920, 1327440512, 1328207744,
	1328975360, 1329742464, 1330510976, 1331279872, 1332049152, 1332819072, 1333589248, 1334359168, 1335130240, 1335901824, 1336673920, 1337446400, 1338218368, 1338991744, 1339765632, 1340539904, 1341314560, 1342088832, 1342864512, 1343640576, 1344417024, 1345193984, 1345971456, 1346748416, 1347526656, 1348305408, 1349084672, 1349864320, 1350643456, 1351424000, 1352205056, 1352986496, 1353768448, 1354550784, 1355332608, 1356115968, 1356899712, 1357683840, 1358468480, 1359252608, 1360038144, 1360824192, 1361610624, 1362397440, 1363183872, 1363971712, 1364760064, 1365548672, 1366337792, 1367127424, 1367916672, 1368707200, 1369498240, 1370289664, 1371081472, 1371873024, 1372665856, 1373459072, 1374252800, 1375047040, 1375840768, 1376635904, 1377431552, 1378227584, 1379024000, 1379820928, 1380617472, 1381415296, 1382213760, 1383012480, 1383811840, 1384610560, 1385410816, 1386211456, 1387012480, 1387814144, 1388615168, 1389417728, 1390220672, 1391024128, 1391827968, 1392632320, 1393436288, 1394241536, 1395047296, 1395853568, 1396660224, 1397466368, 1398274048, 1399082112, 1399890688, 1400699648, 1401508224, 1402318080, 1403128576, 1403939456, 1404750848, 1405562624, 1406374016, 1407186816,
	1408000000, 1408813696, 1409627904, 1410441728, 1411256704, 1412072320, 1412888320, 1413704960, 1414521856, 1415338368, 1416156288, 1416974720, 1417793664, 1418612992, 1419431808, 1420252160, 1421072896, 1421894144, 1422715904, 1423537280, 1424359808, 1425183104, 1426006784, 1426830848, 1427655296, 1428479488, 1429305088, 1430131072, 1430957568, 1431784576, 1432611072, 1433438976, 1434267392, 1435096192, 1435925632, 1436754432, 1437584768, 1438415616, 1439246848, 1440078720, 1440910848, 1441742720, 1442575872, 1443409664, 1444243584, 1445078400, 1445912576, 1446748032, 1447584256, 1448420864, 1449257856, 1450094464, 1450932480, 1451771008, 1452609920, 1453449472, 1454289408, 1455128960, 1455969920, 1456811264, 1457653248, 1458495616, 1459337600, 1460180864, 1461024768, 1461869056, 1462713984, 1463558272, 1464404096, 1465250304, 1466097152, 1466944384, 1467792128, 1468639488, 1469488256, 1470337408, 1471187200, 1472037376, 1472887168, 1473738368, 1474589952, 1475442304, 1476294912, 1477148160, 1478000768, 1478854912, 1479709696, 1480564608, 1481420288, 1482275456, 1483132160, 1483989248, 1484846976, 1485704960, 1486562688, 1487421696, 1488281344, 1489141504, 1490002048, 1490863104,
	1491723776, 1492585856, 1493448448, 1494311424, 1495175040, 1496038144, 1496902656, 1497767808, 1498633344, 1499499392, 1500365056, 1501232128, 1502099712, 1502967808, 1503836416, 1504705536, 1505574016, 1506444032, 1507314688, 1508185856, 1509057408, 1509928576, 1510801280, 1511674240, 1512547840, 1513421952, 1514295680, 1515170816, 1516046464, 1516922624, 1517799296, 1518676224, 1519552896, 1520431104, 1521309824, 1522188928, 1523068800, 1523948032, 1524828672, 1525709824, 1526591616, 1527473792, 1528355456, 1529238784, 1530122496, 1531006720, 1531891712, 1532776832, 1533661824, 1534547968, 1535434880, 1536322304, 1537210112, 1538097408, 1538986368, 1539875840, 1540765696, 1541656192, 1542547072, 1543437440, 1544329472, 1545221888, 1546114944, 1547008384, 1547901440, 1548796032, 1549691136, 1550586624, 1551482752, 1552378368, 1553275520, 1554173184, 1555071232, 1555970048, 1556869248, 1557767936, 1558668288, 1559568896, 1560470272, 1561372032, 1562273408, 1563176320, 1564079616, 1564983424, 1565888000, 1566791808, 1567697408, 1568603392, 1569509760, 1570416896, 1571324416, 1572231424, 1573140096, 1574049152, 1574958976, 1575869184, 1576778752, 1577689984, 1578601728, 1579514112,
	1580426880, 1581339264, 1582253056, 1583167488, 1584082432, 1584997888, 1585913984, 1586829440, 1587746304, 1588663936, 1589582080, 1590500736, 1591418880, 1592338560, 1593258752, 1594179584, 1595100928, 1596021632, 1596944000, 1597866880, 1598790272, 1599714304, 1600638848, 1601562752, 1602488320, 1603414272, 1604340992, 1605268224, 1606194816, 1607123072, 1608051968, 1608981120, 1609911040, 1610841344, 1611771264, 1612702848, 1613634688, 1614567168, 1615500288, 1616432896, 1617367040, 1618301824, 1619237120, 1620172800, 1621108096, 1622044928, 1622982272, 1623920128, 1624858752, 1625797632, 1626736256, 1627676416, 1628616960, 1629558272, 1630499968, 1631441152, 1632384000, 1633327232, 1634271232, 1635215744, 1636159744, 1637105152, 1638051328, 1638998016, 1639945088, 1640892928, 1641840128, 1642788992, 1643738368, 1644688384, 1645638784, 1646588672, 1647540352, 1648492416, 1649445120, 1650398464, 1651351168, 1652305408, 1653260288, 1654215808, 1655171712, 1656128256, 1657084288, 1658041856, 1659000064, 1659958784, 1660918272, 1661876992, 1662837376, 1663798400, 1664759936, 1665721984, 1666683520, 1667646720, 1668610560, 1669574784, 1670539776, 1671505024, 1672470016, 1673436544,
};

#define SAMPLE_16BIT 0x01
#define SAMPLE_UNSIGNED 0x02
#define SAMPLE_LOOP 0x04
#define SAMPLE_PINGPONG 0x08
#define SAMPLE_REVERSE 0x10
#define SAMPLE_SUSTAIN 0x20
#define SAMPLE_ENVELOPE 0x40

#ifdef DEBUG_SAMPLES
#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx)
#else
#define SAMPLE_CONVERT_DEBUG(dx)
#endif

#ifdef DEBUG_MIDI
#define MIDI_EVENT_DEBUG(dx,dy) printf("\r%s, %x\n",dx,dy)
#else
#define MIDI_EVENT_DEBUG(dx,dy)
#endif

#define WM_ERR_MEM		0
#define WM_ERR_STAT		1
#define WM_ERR_LOAD		2
#define WM_ERR_OPEN		3
#define WM_ERR_READ		4
#define WM_ERR_INVALID		5
#define WM_ERR_CORUPT		6
#define WM_ERR_NOT_INIT		7
#define WM_ERR_INVALID_ARG	8

#define MAX_AUTO_AMP 2.0

#define FPBITS 10
#define FPMASK ((1L<<FPBITS)-1L)


void do_note_off (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_note_on (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_aftertouch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_control (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_patch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_channel_pressure (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_pitch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);
void do_message (unsigned char ch, struct _mdi *mdi, unsigned long int ptr);

void (*do_event[])(unsigned char ch, struct _mdi *midifile, unsigned long int ptr) = {
	*do_note_off,
	*do_note_on,
	*do_aftertouch,
	*do_control,
	*do_patch,
	*do_channel_pressure,
	*do_pitch,
	*do_message
};

/*
 * =========================
 * Internal Functions
 * =========================
 */


void
WM_ERROR( const char * func, unsigned long int lne, int wmerno, const char * wmfor, int error) {
	const char * errors[] = {
		"Unable to obtain memory\0",
		"Unable to stat\0",
		"Unable to load\0",
		"Unable to open\0",
		"Unable to read\0",
		"Invalid or Unsuported file format\0",
		"File corrupt\0",
		"Library not Initialized\0",
		"Invalid argument\0"
	};
	if (wmfor != NULL) {
		if (error != 0) {
			fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s %s (%s)\n",func, lne, errors[wmerno], wmfor, strerror(error));
		} else {
			fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s %s\n",func, lne, errors[wmerno], wmfor);
		}
	} else {
		if (error != 0) {
			fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s (%s)\n",func, lne, errors[wmerno], strerror(error));
		} else {
			fprintf(stderr,"\rlibWildMidi(%s:%lu): ERROR %s\n",func, lne, errors[wmerno]);
		}
	}

}

unsigned char *
WM_BufferFile (const char *filename, unsigned long int *size) {
	int buffer_fd;
	char *data;
	//struct stat buffer_stat;
	file_t temp_fd;
	size_t temp_size;
#ifndef _WIN32
	//char *home = NULL;
	//struct passwd *pwd_ent;
	char buffer_dir[1024];
#endif

	char *buffer_file = malloc(strlen(filename) + 1);

	if (buffer_file == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno);
		return NULL;
	}

	strcpy (buffer_file, filename);
#ifndef _WIN32
/*	if (strncmp(buffer_file,"~/",2) == 0) {
		if ((pwd_ent = getpwuid (getuid ()))) {
			home = pwd_ent->pw_dir;
		} else {
			home = getenv ("HOME");
		}
		if (home) {
			buffer_file = realloc(buffer_file,(strlen(buffer_file) + strlen(home) + 1));
			if (buffer_file == NULL) {
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno);
				free(buffer_file);
				return NULL;
			}
			memmove((buffer_file + strlen(home)), (buffer_file + 1), (strlen(buffer_file)));
			strncpy (buffer_file, home,strlen(home));
		}
	} else  */
	if (buffer_file[0] != '/') {
		getcwd(buffer_dir,1024);
		if (buffer_dir[strlen(buffer_dir)-1] != '/') {
			buffer_dir[strlen(buffer_dir)+1] = '\0';
			buffer_dir[strlen(buffer_dir)] = '/';
		}
		buffer_file = realloc(buffer_file,(strlen(buffer_file) + strlen(buffer_dir) + 1));
		if (buffer_file == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno);
			free(buffer_file);
			return NULL;
		}
		memmove((buffer_file + strlen(buffer_dir)), buffer_file, strlen(buffer_file)+1);
		strncpy (buffer_file,buffer_dir,strlen(buffer_dir));
	}
#endif
	temp_fd = fs_open(buffer_file, O_RDONLY);
	if (temp_fd) {
		temp_size = fs_total(temp_fd);
		fs_close(temp_fd);
	} else {
	//if (stat(buffer_file,&buffer_stat)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_STAT, filename, errno);
		free(buffer_file);
		return NULL;
	}
	*size = temp_size; //buffer_stat.st_size;
	data = malloc(*size);
	if (data == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno);
		free(buffer_file);
		return NULL;
	}
#ifdef _WIN32
	if ((buffer_fd = open(buffer_file,(O_RDONLY | O_BINARY))) == -1) {
#else
	if ((buffer_fd = open(buffer_file,O_RDONLY)) == -1) {
#endif
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_OPEN, filename, errno);
		free(buffer_file);
		free(data);
		return NULL;
	}
	if (read(buffer_fd,data,*size) != *size) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_READ, filename, errno);
		free(buffer_file);
		free(data);
		close(buffer_fd);
		return NULL;
	}
	close(buffer_fd);
	free(buffer_file);
	return data;
}

inline void
WM_Lock (int * wmlock) {
	LOCK_START:
	if (__builtin_expect(((*wmlock) == 0),1)) {
		(*wmlock)++;
		if (__builtin_expect(((*wmlock) == 1), 1)) {
			return;
		}
		(*wmlock)--;
	}
#ifdef _WIN32
	Sleep(10);
#else
	//usleep(500);
	thd_sleep(1);
#endif
	goto LOCK_START;
}

inline void
WM_Unlock (int *wmlock) {
	(*wmlock)--;
}

void
WM_InitPatches ( void ) {
	int i;
	for (i = 0; i < 128; i++) {
		patch[i] = NULL;
	}
}

void
WM_FreePatches ( void ) {
	int i;
	struct _patch * tmp_patch;
	struct _sample * tmp_sample;

	WM_Lock(&patch_lock);
	for (i = 0; i < 128; i++) {
		if (patch[i] != NULL) {
			while (patch[i] != NULL) {
				if (patch[i]->filename != NULL) {
					if (patch[i]->first_sample != NULL) {
						while (patch[i]->first_sample != NULL) {
							tmp_sample = patch[i]->first_sample->next;
							if (patch[i]->first_sample->data != NULL)
								free (patch[i]->first_sample->data);
							free (patch[i]->first_sample);
							patch[i]->first_sample = tmp_sample;
						}
					}
					free (patch[i]->filename);
				}
				tmp_patch = patch[i]->next;
				free(patch[i]);
				patch[i] = tmp_patch;
			}
		}
	}
	WM_Unlock(&patch_lock);
}

int
WM_LoadConfig (const char *config_file) {
	unsigned long int config_size = 0;
	unsigned char *config_buffer =  NULL;
	char * dir_end =  NULL;
	char * config_dir =  NULL;
	unsigned long int config_ptr = 0;
	unsigned long int line_start_ptr = 0;
	char * line_buffer = NULL;
	unsigned long int line_ptr = 0;
	char * chr_ptr = NULL;
	unsigned short int patchid = 0;
	char * new_config = NULL;
	struct _patch * tmp_patch;

	if ((config_buffer = WM_BufferFile(config_file, &config_size)) == NULL) {
		return -1;
	}
	if (config_buffer == NULL) {
		WM_FreePatches();
		return -1;
	}

	dir_end = strrchr(config_file,'/');
	if (dir_end != NULL) {
		config_dir = malloc((dir_end - config_file + 2));
		if (config_dir == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno);
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
			WM_FreePatches();
			free (config_buffer);
			return -1;
		}
		strncpy(config_dir, config_file, (dir_end - config_file + 1));
		config_dir[dir_end - config_file + 1] = '\0';
	}
	config_ptr = 0;
	line_start_ptr = 0;
	while (config_ptr < config_size) {
		// find end of line
		if (config_buffer[config_ptr] != '\n') {
			// remove unwanted crud
			if (config_buffer[config_ptr] == '\t') {
				config_buffer[config_ptr] = ' ';
			} else if (config_buffer[config_ptr] == '\r') {
				config_buffer[config_ptr] = ' ';
			}
			if ((config_buffer[config_ptr] == ' ') && (config_ptr == line_start_ptr)) {
				line_start_ptr++;
			}

			config_ptr++;
			continue;
		}
		config_buffer[config_ptr] = '\0';
		if (config_ptr == line_start_ptr) {
			config_ptr++;
			line_start_ptr++;
			continue;
		}
		if (config_buffer[line_start_ptr] == '#') {
			config_ptr++;
			line_start_ptr = config_ptr;
			continue;
		}

		// copy line into a workable buffer
		line_buffer = realloc(line_buffer, (config_ptr - line_start_ptr + 1));
		if (line_buffer == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno);
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
			WM_FreePatches();
			if (config_dir != NULL)
				free(config_dir);
			free (config_buffer);
			return -1;
		}
		strcpy(line_buffer, &config_buffer[line_start_ptr]);
		config_ptr++;
		line_start_ptr = config_ptr;
		// remove unwnted crud from line for easier parsing
		if ((chr_ptr = strstr(line_buffer,"  ")) != NULL) {
			while ((chr_ptr = strstr(line_buffer,"  ")) != NULL) {
				memmove(chr_ptr, &chr_ptr[1], strlen(chr_ptr));
			}
		}
		if ((chr_ptr = strchr(line_buffer, '#')) != NULL) {
			*chr_ptr = '\0';
		}
		if (line_buffer[strlen(line_buffer) -1] == ' ') {
			while (line_buffer[strlen(line_buffer) -1] == ' ') {
				line_buffer[strlen(line_buffer) -1] = '\0';
			}
		}

		// now parse line
		if (strncasecmp(line_buffer, "dir ", 4) == 0) {
			if (line_buffer[strlen(line_buffer) - 1] == '/') {
				config_dir = realloc(config_dir, strlen(&line_buffer[4]) + 1);
				if (config_dir == NULL) {
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno);
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
					WM_FreePatches();
					free (line_buffer);
					free (config_buffer);
					return -1;
				}
				strcpy(config_dir, &line_buffer[4]);
			} else {
				config_dir = realloc(config_dir, strlen(&line_buffer[4]) + 2);
				if (config_dir == NULL) {
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno);
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
					WM_FreePatches();
					free (line_buffer);
					free (config_buffer);
					return -1;
				}
				strcpy(config_dir, &line_buffer[4]);
				strcat(config_dir,"/");
			}
			continue;
		} else if (strncasecmp(line_buffer, "source ", 7) == 0) {
			if (config_dir != NULL) {
				new_config = malloc(strlen(config_dir) + strlen(&line_buffer[7]) + 1);
				if (new_config == NULL) {
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno);
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
					WM_FreePatches();
					free (config_dir);
					free (line_buffer);
					free (config_buffer);
					return -1;
				}
				strcpy(new_config,config_dir);
				strcpy(&new_config[strlen(config_dir)], &line_buffer[7]);
			} else {
				new_config = malloc(strlen(&line_buffer[7]) + 1);
				if (new_config == NULL) {
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno);
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
					WM_FreePatches();
					free (line_buffer);
					free (config_buffer);
					return -1;
				}
				strcpy(new_config, &line_buffer[7]);
			}
			if (WM_LoadConfig(new_config) == -1) {
				free (new_config);
				free (line_buffer);
				free (config_buffer);
				if (config_dir != NULL)
					free (config_dir);
				return -1;
			}
			free (new_config);
			continue;
		} else if (strncasecmp(line_buffer, "bank ", 5) == 0) {
			if (!isdigit(line_buffer[5])) {
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in bank line)", 0);
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
				WM_FreePatches();
				if (config_dir != NULL)
					free (config_dir);
				free (line_buffer);
				free (config_buffer);
				return -1;
			}
			patchid = (atoi(&line_buffer[5]) & 0xFF ) << 8;
			continue;
		} else if (strncasecmp(line_buffer, "drumset ", 8) == 0) {
			if (!isdigit(line_buffer[8])) {
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in drumset line)", 0);
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
				WM_FreePatches();
				if (config_dir != NULL)
					free (config_dir);
				free (line_buffer);
				free (config_buffer);
				return -1;
			}
			patchid = ((atoi(&line_buffer[8]) & 0xFF ) << 8) | 0x80;
			continue;
		} else if (isdigit(line_buffer[0])) {
			patchid = (patchid & 0xFF80) | (atoi(line_buffer) & 0x7F);
			if (patch[(patchid & 0x7F)] == NULL) {
				patch[(patchid & 0x7F)] = malloc (sizeof(struct _patch));
				if (patch[(patchid & 0x7F)] == NULL) {
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
					WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
					WM_FreePatches();
					if (config_dir != NULL)
						free (config_dir);
					free (line_buffer);
					free (config_buffer);
					return -1;
				}
				tmp_patch = patch[(patchid & 0x7F)];
				tmp_patch->patchid = patchid;
				tmp_patch->filename = NULL;
				tmp_patch->amp = 1024;
				tmp_patch->note = 0;
				tmp_patch->next = NULL;
				tmp_patch->first_sample = NULL;
				tmp_patch->loaded = 0;
				tmp_patch->inuse_count = 0;
			} else {
				tmp_patch = patch[(patchid & 0x7F)];
				if (tmp_patch->patchid == patchid) {
					free (tmp_patch->filename);
					tmp_patch->filename = NULL;
					tmp_patch->amp = 1024;
					tmp_patch->note = 0;
				} else {
					if (tmp_patch->next != NULL) {
						while (tmp_patch->next != NULL) {
							if (tmp_patch->next->patchid == patchid)
								break;
							tmp_patch = tmp_patch->next;
						}
						if (tmp_patch->next == NULL) {
							tmp_patch->next = malloc (sizeof(struct _patch));
							if (tmp_patch->next == NULL) {
								WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
								WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
								WM_FreePatches();
								if (config_dir != NULL)
									free (config_dir);
								free (line_buffer);
								free (config_buffer);
								return -1;
							}
							tmp_patch = tmp_patch->next;
							tmp_patch->patchid = patchid;
							tmp_patch->filename = NULL;
							tmp_patch->amp = 1024;
							tmp_patch->note = 0;
							tmp_patch->next = NULL;
							tmp_patch->first_sample = NULL;
							tmp_patch->loaded = 0;
							tmp_patch->inuse_count = 0;
						} else {
							tmp_patch = tmp_patch->next;
							free (tmp_patch->filename);
							tmp_patch->filename = NULL;
							tmp_patch->amp = 1024;
							tmp_patch->note = 0;
						}
					} else {
						tmp_patch->next = malloc (sizeof(struct _patch));
						if (tmp_patch->next == NULL) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
							WM_FreePatches();
							if (config_dir != NULL)
								free (config_dir);
							free (line_buffer);
							free (config_buffer);
							return -1;
						}
						tmp_patch = tmp_patch->next;
						tmp_patch->patchid = patchid;
						tmp_patch->filename = NULL;
						tmp_patch->amp = 1024;
						tmp_patch->note = 0;
						tmp_patch->next = NULL;
						tmp_patch->first_sample = NULL;
						tmp_patch->loaded = 0;
						tmp_patch->inuse_count = 0;
					}
				}
			}

			chr_ptr = strchr(line_buffer,' ') + 1;
			if (chr_ptr == NULL) {
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
				WM_FreePatches();
				if (config_dir != NULL)
					free (config_dir);
				free (line_buffer);
				free (config_buffer);
				return -1;
			}
			line_ptr = chr_ptr - line_buffer;
			chr_ptr = strchr(&line_buffer[line_ptr],' ');
			if (chr_ptr != NULL) {
				*chr_ptr = '\0';
			}
			if (strncasecmp(&line_buffer[(line_ptr + strlen(&line_buffer[line_ptr]) - 5)], ".pat", 4) != 0) {
				if (config_dir != NULL) {
					tmp_patch->filename = malloc(strlen(config_dir) + strlen(&line_buffer[line_ptr]) + 5);
					if (tmp_patch->filename == NULL) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
						WM_FreePatches();
						if (config_dir != NULL)
							free (config_dir);
						free (line_buffer);
						free (config_buffer);
						return -1;
					}
					strcpy(tmp_patch->filename, config_dir);
					strcat(tmp_patch->filename, &line_buffer[line_ptr]);
				} else {
					tmp_patch->filename = malloc(strlen(&line_buffer[line_ptr]) + 5);
					if (tmp_patch->filename == NULL) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
						WM_FreePatches();
						free (line_buffer);
						free (config_buffer);
						return -1;
					}
					strcpy(tmp_patch->filename, &line_buffer[line_ptr]);
				}
				strcat(tmp_patch->filename, ".pat");
			} else {
				if (config_dir != NULL) {
					tmp_patch->filename = malloc(strlen(config_dir) + strlen(&line_buffer[line_ptr]) + 1);
					if (tmp_patch->filename == NULL) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
						WM_FreePatches();
						if (config_dir != NULL)
							free (config_dir);
						free (line_buffer);
						free (config_buffer);
						return -1;
					}
					strcpy(tmp_patch->filename, config_dir);
					strcat(tmp_patch->filename, &line_buffer[line_ptr]);
				} else {
					tmp_patch->filename = malloc(strlen(&line_buffer[line_ptr]) + 1);
					if (tmp_patch->filename == NULL) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
						WM_FreePatches();
						free (line_buffer);
						free (config_buffer);
						return -1;
					}
					strcpy(tmp_patch->filename, &line_buffer[line_ptr]);
				}
			}

			tmp_patch->env[0].set = 0x00;
			tmp_patch->env[1].set = 0x00;
			tmp_patch->env[2].set = 0x00;
			tmp_patch->env[3].set = 0x00;
			tmp_patch->env[4].set = 0x00;
			tmp_patch->env[5].set = 0x00;
			tmp_patch->keep = 0;
			tmp_patch->remove = 0;

			if (chr_ptr != NULL) {
				line_ptr = chr_ptr - line_buffer + 1;
				chr_ptr = strstr(&line_buffer[line_ptr], "amp=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[4])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->amp = ((atoi(&chr_ptr[4]) << 10) / 100);
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "note=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[5])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->note = atoi(&chr_ptr[5]);
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "keep=loop");
				if (chr_ptr != NULL) {
					tmp_patch->keep |= SAMPLE_LOOP;
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "keep=env");
				if (chr_ptr != NULL) {
					tmp_patch->keep |= SAMPLE_ENVELOPE;
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "remove=sustain");
				if (chr_ptr != NULL) {
					tmp_patch->remove |= SAMPLE_SUSTAIN;
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_time0=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[10])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[0].time = atof(&chr_ptr[10]);
						if ((tmp_patch->env[0].time > 45000.0) || (tmp_patch->env[0].time < 1.47)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[0].set &= 0xFE;
						} else {
							tmp_patch->env[0].set |= 0x01;
						}
					}
				}

				chr_ptr = strstr(&line_buffer[line_ptr], "env_time1=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[10])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[1].time = atof(&chr_ptr[10]);
						if ((tmp_patch->env[1].time > 45000.0) || (tmp_patch->env[1].time < 1.47)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[1].set &= 0xFE;
						} else {
							tmp_patch->env[1].set |= 0x01;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_time2=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[10])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[2].time = atof(&chr_ptr[10]);
						if ((tmp_patch->env[2].time > 45000.0) || (tmp_patch->env[2].time < 1.47)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[2].set &= 0xFE;
						} else {
							tmp_patch->env[2].set |= 0x01;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_time3=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[10])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[3].time = atof(&chr_ptr[10]);
						if ((tmp_patch->env[3].time > 45000.0) || (tmp_patch->env[3].time < 1.47)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[3].set &= 0xFE;
						} else {
							tmp_patch->env[3].set |= 0x01;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_time4=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[10])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[4].time = atof(&chr_ptr[10]);
						if ((tmp_patch->env[4].time > 45000.0) || (tmp_patch->env[4].time < 1.47)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[4].set &= 0xFE;
						} else {
							tmp_patch->env[4].set |= 0x01;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_time5=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[10])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[5].time = atof(&chr_ptr[10]);
						if ((tmp_patch->env[5].time > 45000.0) || (tmp_patch->env[5].time < 1.47)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[5].set &= 0xFE;
						} else {
							tmp_patch->env[5].set |= 0x01;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_level0=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[11])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[0].level = atof(&chr_ptr[10]);
						if ((tmp_patch->env[0].level > 1.0) || (tmp_patch->env[0].level < 0.0)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[0].set &= 0xFD;
						} else {
							tmp_patch->env[0].set |= 0x02;
						}
					}
				}

				chr_ptr = strstr(&line_buffer[line_ptr], "env_level1=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[11])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[1].level = atof(&chr_ptr[10]);
						if ((tmp_patch->env[1].level > 1.0) || (tmp_patch->env[1].level < 0.0)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[1].set &= 0xFD;
						} else {
							tmp_patch->env[1].set |= 0x02;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_level2=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[11])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[2].level = atof(&chr_ptr[10]);
						if ((tmp_patch->env[2].level > 1.0) || (tmp_patch->env[2].level < 0.0)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[2].set &= 0xFD;
						} else {
							tmp_patch->env[2].set |= 0x02;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_level3=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[11])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[3].level = atof(&chr_ptr[10]);
						if ((tmp_patch->env[3].level > 1.0) || (tmp_patch->env[3].level < 0.0)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[3].set &= 0xFD;
						} else {
							tmp_patch->env[3].set |= 0x02;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_level4=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[11])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[4].level = atof(&chr_ptr[10]);
						if ((tmp_patch->env[4].level > 1.0) || (tmp_patch->env[4].level < 0.0)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[4].set &= 0xFD;
						} else {
							tmp_patch->env[4].set |= 0x02;
						}
					}
				}
				chr_ptr = strstr(&line_buffer[line_ptr], "env_level5=");
				if (chr_ptr != NULL) {
					if (!isdigit(chr_ptr[11])) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(syntax error in patch line)", 0);
					} else {
						tmp_patch->env[5].level = atof(&chr_ptr[10]);
						if ((tmp_patch->env[5].level > 1.0) || (tmp_patch->env[5].level < 0.0)) {
							WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, "(range error in patch line)", 0);
							tmp_patch->env[5].set &= 0xFD;
						} else {
							tmp_patch->env[5].set |= 0x02;
						}
					}
				}

			}
		}
	}

	if (line_buffer != NULL)
		free (line_buffer);

	free (config_buffer);

	if (config_dir != NULL)
		free (config_dir);

	return 0;
}

/*
 * sample data conversion functions
 * convert data to signed shorts
 */

/* 8bit signed */
int
convert_8s (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data++) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit signed ping pong */
int
convert_8sp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data++) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = (*read_data++ << 8);
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = (*read_data++) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = (*read_data++ << 8);
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b = (*read_data++) << 8;
				if (*write_data_b > gus_sample->max_peek) {
					gus_sample->max_peek = *write_data_b;
				} else if (*write_data_b < gus_sample->min_peek) {
					gus_sample->min_peek = *write_data_b;
				}
				write_data_b++;
			} while (read_data != read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit signed reverse */
int
convert_8sr (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + gus_sample->data_length - 1;
		do {
			*write_data = (*read_data++) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data--;
		} while (read_data != read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->modes ^= SAMPLE_REVERSE;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}


/* 8bit signed reverse ping pong */
int
convert_8srp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data--) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = (*read_data-- << 8);
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = (*read_data--) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = (*read_data-- << 8);
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b = (*read_data--) << 8;
			if (*write_data_b > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data_b;
			} else if (*write_data_b < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data_b;
			}
			write_data_b++;
		} while (read_data != read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned */
int
convert_8u (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = ((*read_data++) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);
		gus_sample->modes ^= SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned ping pong */
int
convert_8up (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = ((*read_data++) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = ((*read_data++) ^ 0x80) << 8;
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = ((*read_data++) ^ 0x80) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = ((*read_data++) ^ 0x80) << 8;
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b = ((*read_data++) ^ 0x80) << 8;
				if (*write_data_b > gus_sample->max_peek) {
					gus_sample->max_peek = *write_data_b;
				} else if (*write_data_b < gus_sample->min_peek) {
					gus_sample->min_peek = *write_data_b;
				}
				write_data_b++;
			} while (read_data != read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_UNSIGNED;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned reverse */
int
convert_8ur (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((gus_sample->data_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + gus_sample->data_length - 1;
		do {
			*write_data = ((*read_data++) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data--;
		} while (read_data != read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 8bit unsigned reverse ping pong */
int
convert_8urp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc((new_length + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		write_data_a = write_data + dloop_length;
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + dloop_length;
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data != read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b = ((*read_data--) ^ 0x80) << 8;
			if (*write_data_b > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data_b;
			} else if (*write_data_b < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data_b;
			}
			write_data_b++;
		} while (read_data != read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit signed */
int
convert_16s (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1),sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = *read_data++;
			*write_data |= (*read_data++) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit signed ping pong */
int
convert_16sp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data++);
			*write_data |= (*read_data++) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = (*read_data++);
		*write_data |= (*read_data++) << 8;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = (*read_data++);
			*write_data |= (*read_data++) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = *(read_data++);
		*write_data |= (*read_data++) << 8;
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b = *(read_data++);
				*write_data_b |= (*read_data++) << 8;
				if (*write_data_b > gus_sample->max_peek) {
					gus_sample->max_peek = *write_data_b;
				} else if (*write_data_b < gus_sample->min_peek) {
					gus_sample->min_peek = *write_data_b;
				}
				write_data_b++;
			} while (read_data < read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG;
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit signed reverse */
int
convert_16sr (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1;
		do {
			*write_data = *read_data++;
			*write_data |= (*read_data++) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data--;
		} while (read_data < read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		gus_sample->modes ^= SAMPLE_REVERSE;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}


/* 16bit signed reverse ping pong */
int
convert_16srp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {

			*write_data = (*read_data--) << 8;
			*write_data |= *read_data--;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = (*read_data-- << 8);
		*write_data |= *read_data--;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = (*read_data--) << 8;
			*write_data |= *read_data--;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = ((*read_data--) << 8);
		*write_data |= *read_data--;
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b = (*read_data--) << 8;
			*write_data_b |= *read_data--;
			if (*write_data_b > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data_b;
			} else if (*write_data_b < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data_b;
			}
			write_data_b++;
		} while (read_data < read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned */
int
convert_16u (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1),sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = *read_data++;
			*write_data |= ((*read_data++) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		gus_sample->modes ^= SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned ping pong */
int
convert_16up (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->loop_start;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = (*read_data++);
			*write_data |= ((*read_data++) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = (*read_data++);
		*write_data |= ((*read_data++) ^ 0x80) << 8;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_end;
		do {
			*write_data = (*read_data++);
			*write_data |= ((*read_data++) ^ 0x80) << 8;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = (*read_data++);
		*write_data |= ((*read_data++) ^ 0x80) << 8;
		*write_data_b++ = *write_data;
		read_end = data + gus_sample->data_length;
		if (__builtin_expect((read_data != read_end),1)) {
			do {
				*write_data_b = (*read_data++);
				*write_data_b |= ((*read_data++) ^ 0x80) << 8;
				if (*write_data_b > gus_sample->max_peek) {
					gus_sample->max_peek = *write_data_b;
				} else if (*write_data_b < gus_sample->min_peek) {
					gus_sample->min_peek = *write_data_b;
				}
				write_data_b++;
			} while (read_data < read_end);
		}
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG;
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned reverse */
int
convert_16ur (unsigned char *data, struct _sample *gus_sample ) {
	unsigned char *read_data = data;
	unsigned char *read_end = data + gus_sample->data_length;
	signed short int *write_data = NULL;
	unsigned long int tmp_loop = 0;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((gus_sample->data_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data + (gus_sample->data_length >> 1) - 1;
		do {
			*write_data = *read_data++;
			*write_data |= ((*read_data++) ^ 0x80) << 8;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data--;
		} while (read_data < read_end);
		tmp_loop = gus_sample->loop_end;
		gus_sample->loop_end = gus_sample->data_length - gus_sample->loop_start;
		gus_sample->loop_start = gus_sample->data_length - tmp_loop;
		gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		gus_sample->loop_start >>= 1;
		gus_sample->loop_end >>= 1;
		gus_sample->data_length >>= 1;
		gus_sample->modes ^= SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}
	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}

/* 16bit unsigned reverse ping pong */
int
convert_16urp (unsigned char *data, struct _sample *gus_sample ) {
	unsigned long int loop_length = gus_sample->loop_end - gus_sample->loop_start;
	unsigned long int dloop_length = loop_length * 2;
	unsigned long int new_length = gus_sample->data_length + dloop_length;
	unsigned char *read_data = data + gus_sample->data_length - 1;
	unsigned char *read_end = data + gus_sample->loop_end;
	signed short int *write_data = NULL;
	signed short int *write_data_a = NULL;
	signed short int *write_data_b = NULL;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	gus_sample->data = calloc(((new_length >> 1) + 1), sizeof(signed short int));
	if (__builtin_expect((gus_sample->data != NULL),1)) {
		write_data = gus_sample->data;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			*write_data |= *read_data--;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		*write_data |= *read_data--;
		write_data_a = write_data + (dloop_length >> 1);
		*write_data_a-- = *write_data;
		write_data++;
		write_data_b = write_data + (dloop_length >> 1);
		read_end = data + gus_sample->loop_start;
		do {
			*write_data = ((*read_data--) ^ 0x80) << 8;
			*write_data |= *read_data--;
			*write_data_a-- = *write_data;
			*write_data_b++ = *write_data;
			if (*write_data > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data;
			} else if (*write_data < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data;
			}
			write_data++;
		} while (read_data < read_end);

		*write_data = ((*read_data--) ^ 0x80) << 8;
		*write_data |= *read_data--;
		*write_data_b++ = *write_data;
		read_end = data - 1;
		do {
			*write_data_b = ((*read_data--) ^ 0x80) << 8;
			*write_data_b |= *read_data--;
			if (*write_data_b > gus_sample->max_peek) {
				gus_sample->max_peek = *write_data_b;
			} else if (*write_data_b < gus_sample->min_peek) {
				gus_sample->min_peek = *write_data_b;
			}
			write_data_b++;
		} while (read_data < read_end);
		gus_sample->loop_start += loop_length;
		gus_sample->loop_end += dloop_length;
		gus_sample->data_length = new_length;
		gus_sample->modes ^= SAMPLE_PINGPONG | SAMPLE_REVERSE | SAMPLE_UNSIGNED;
		return 0;
	}

	WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno);
	return -1;
}


/* sample loading */

int
load_sample (struct _patch *sample_patch) {
	unsigned char *gus_patch;
	unsigned long int gus_size;
	unsigned long int gus_ptr;
	unsigned char no_of_samples;
	struct _sample *gus_sample = NULL;
	unsigned long int i = 0;

	int (*do_convert[])(unsigned char *data, struct _sample *gus_sample ) = {
		convert_8s,
		convert_16s,
		convert_8u,
		convert_16u,
		convert_8sp,
		convert_16sp,
		convert_8up,
		convert_16up,
		convert_8sr,
		convert_16sr,
		convert_8ur,
		convert_16ur,
		convert_8srp,
		convert_16srp,
		convert_8urp,
		convert_16urp
	};
	unsigned long int tmp_loop;

	SAMPLE_CONVERT_DEBUG(__FUNCTION__);
	SAMPLE_CONVERT_DEBUG(sample_patch->filename);
	sample_patch->loaded = 1;
	if ((gus_patch = WM_BufferFile(sample_patch->filename,&gus_size)) == NULL) {
		return -1;
	}
	if (gus_size < 239) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0);
		free(gus_patch);
		return -1;
	}
	if (memcmp(gus_patch, "GF1PATCH110\0ID#000002", 22) && memcmp(gus_patch, "GF1PATCH100\0ID#000002", 22)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0);
		free(gus_patch);
		return -1;
	}
	if (gus_patch[82] > 1) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0);
		free(gus_patch);
		return -1;
	}
	if (gus_patch[151] > 1) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(unsupported format)", 0);
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0);
		free(gus_patch);
		return -1;
	}

	no_of_samples = gus_patch[198];
	sample_patch->first_sample = NULL;
	gus_ptr = 239;
	while (no_of_samples) {
		unsigned long int tmp_cnt;
		if (sample_patch->first_sample == NULL) {
			sample_patch->first_sample = malloc(sizeof(struct _sample));
			gus_sample = sample_patch->first_sample;
		} else {
			gus_sample->next = malloc(sizeof(struct _sample));
			gus_sample = gus_sample->next;
		}
		if (gus_sample == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0);
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, sample_patch->filename, 0);
			return -1;
		}

		gus_sample->next = NULL;
		gus_sample->loop_fraction = gus_patch[gus_ptr+7];
		gus_sample->data_length = (gus_patch[gus_ptr+11] << 24) | (gus_patch[gus_ptr+10] << 16) | (gus_patch[gus_ptr+9] << 8) | gus_patch[gus_ptr+8];
		gus_sample->loop_start = (gus_patch[gus_ptr+15] << 24) | (gus_patch[gus_ptr+14] << 16) | (gus_patch[gus_ptr+13] << 8) | gus_patch[gus_ptr+12];
		gus_sample->loop_end = (gus_patch[gus_ptr+19] << 24) | (gus_patch[gus_ptr+18] << 16) | (gus_patch[gus_ptr+17] << 8) | gus_patch[gus_ptr+16];
		gus_sample->rate = (gus_patch[gus_ptr+21] << 8) | gus_patch[gus_ptr+20];
		gus_sample->freq_low = ((gus_patch[gus_ptr+25] << 24) | (gus_patch[gus_ptr+24] << 16) | (gus_patch[gus_ptr+23] << 8) | gus_patch[gus_ptr+22]);
		gus_sample->freq_high = ((gus_patch[gus_ptr+29] << 24) | (gus_patch[gus_ptr+28] << 16) | (gus_patch[gus_ptr+27] << 8) | gus_patch[gus_ptr+26]);
		gus_sample->freq_root = ((gus_patch[gus_ptr+33] << 24) | (gus_patch[gus_ptr+32] << 16) | (gus_patch[gus_ptr+31] << 8) | gus_patch[gus_ptr+30]);

		/* This is done this way instead of ((freq * 1024) / rate) to avoid 32bit overflow. */
		/* Result is 0.001% inacurate */
		gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2;

#if 0
		printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n",
			gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]);
		printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n",
			gus_patch[gus_ptr+52], gus_patch[gus_ptr+53], gus_patch[gus_ptr+54]);
#endif
		gus_sample->modes = gus_patch[gus_ptr+55] & 0x7F;
		if ((sample_patch->remove & SAMPLE_SUSTAIN) && (gus_sample->modes & SAMPLE_SUSTAIN)) {
			gus_sample->modes ^= SAMPLE_SUSTAIN;
		}
		if (sample_patch->patchid & 0x0080) {
			if (!(sample_patch->keep & SAMPLE_LOOP)) {
 				gus_sample->modes &= 0xFB;
			}
			if (!(sample_patch->keep & SAMPLE_ENVELOPE)) {
				gus_sample->modes &= 0xBF;
			}
		}


		if (gus_sample->loop_start > gus_sample->loop_end) {
			tmp_loop = gus_sample->loop_end;
			gus_sample->loop_end = gus_sample->loop_start;
			gus_sample->loop_start = tmp_loop;
			gus_sample->loop_fraction  = ((gus_sample->loop_fraction & 0x0f) << 4) | ((gus_sample->loop_fraction & 0xf0) >> 4);
		}
		for (i = 0; i < 6; i++) {
			if (gus_sample->modes & SAMPLE_ENVELOPE) {
				unsigned char env_rate = gus_patch[gus_ptr+37+i];
				if (sample_patch->env[i].set & 0x02) {
					gus_sample->env_target[i] = 16448 * (unsigned long int)(255.0 * sample_patch->env[i].level);
				} else {
					gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr+43+i];
				}

				if (sample_patch->env[i].set & 0x01) {
					gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * (sample_patch->env[i].time / 1000.0)));
				} else {
					gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[env_rate]));
					if (gus_sample->env_rate[i] == 0) {
						fprintf(stderr,"\rWarning: libWildMidi %s found invalid envelope(%lu) rate setting in %s. Using %f instead.\n",__FUNCTION__, i, sample_patch->filename, env_time_table[63]);
						gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63]));
					}
				}
			} else {
				gus_sample->env_target[i] = 4194303;
				gus_sample->env_rate[i]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63]));
			}
		}

		gus_sample->env_target[6] = 0;
		gus_sample->env_rate[6]  = (unsigned long int)(4194303.0 / ((float)WM_SampleRate * env_time_table[63]));

		if ((sample_patch->patchid == 47) && (!(gus_sample->modes & SAMPLE_LOOP))) {
			for (i = 3; i < 6; i++) {
				gus_sample->env_target[i] = gus_sample->env_target[2];
				gus_sample->env_rate[i] = gus_sample->env_rate[2];
			}
		}

		gus_ptr += 96;
		tmp_cnt = gus_sample->data_length;

/* convert to float */
		gus_sample->min_peek = 0;
		gus_sample->max_peek = 0;

		if (do_convert[(((gus_sample->modes & 0x18) >> 1)| (gus_sample->modes & 0x03))](&gus_patch[gus_ptr], gus_sample) == -1) {
			return -1;

		};

		if (gus_sample->max_peek > (-gus_sample->min_peek)) {
			gus_sample->peek_adjust = 33553408 / gus_sample->max_peek;
		} else {
			gus_sample->peek_adjust = 33554432 / (-gus_sample->min_peek);
		}
		gus_sample->peek_adjust = (gus_sample->peek_adjust * sample_patch->amp) >> 10;

		gus_ptr += tmp_cnt;
		gus_sample->loop_start = (gus_sample->loop_start << 10) | (((gus_sample->loop_fraction & 0x0f) << 10) / 16);
		gus_sample->loop_end = (gus_sample->loop_end << 10) | (((gus_sample->loop_fraction & 0xf0) << 6) / 16);
		gus_sample->loop_size = gus_sample->loop_end - gus_sample->loop_start;
		gus_sample->data_length = gus_sample->data_length << 10;
		no_of_samples--;
	}
	free(gus_patch);
	return 0;
}


struct _patch *
get_patch_data(struct _mdi *mdi, unsigned short patchid) {
	struct _patch *search_patch;

	WM_Lock(&patch_lock);

	search_patch = patch[patchid & 0x007F];

	if (search_patch == NULL) {
		WM_Unlock(&patch_lock);
		return NULL;
	}

	while(search_patch != NULL) {
		if (search_patch->patchid == patchid) {
			WM_Unlock(&patch_lock);
			return search_patch;
		}
		search_patch = search_patch->next;
	}
	if ((patchid >> 8) != 0) {
		WM_Unlock(&patch_lock);
		return (get_patch_data(mdi, patchid & 0x00FF));
	}
	WM_Unlock(&patch_lock);
	return NULL;
}

void
load_patch (struct _mdi *mdi, unsigned short patchid) {
	int i;
	struct _patch *tmp_patch = NULL;

	for (i = 0; i < mdi->patch_count; i++) {
		if (mdi->patches[i]->patchid == patchid) {
			return;
		}
	}

	tmp_patch = get_patch_data(mdi, patchid);
	if (tmp_patch == NULL) {
		return;
	}

	WM_Lock(&patch_lock);
	if (!tmp_patch->loaded) {
		if (load_sample(tmp_patch) == -1) {
			WM_Unlock(&patch_lock);
			return;
		}
	}

	if (tmp_patch->first_sample == NULL) {
		WM_Unlock(&patch_lock);
		return;
	}

	mdi->patch_count++;
	mdi->patches = realloc(mdi->patches, (sizeof(struct _patch) * mdi->patch_count));
	mdi->patches[mdi->patch_count -1] = tmp_patch;
	tmp_patch->inuse_count++;
	WM_Unlock(&patch_lock);
	return;
}


struct _sample *
get_sample_data (struct _patch *sample_patch, unsigned long int freq) {
	struct _sample *last_sample = NULL;
	struct _sample *return_sample = NULL;

	WM_Lock(&patch_lock);
	if (sample_patch == NULL) {
		WM_Unlock(&patch_lock);
		return NULL;
	}
	if (sample_patch->first_sample == NULL) {
		WM_Unlock(&patch_lock);
		return NULL;
	}
	if (freq == 0) {
		WM_Unlock(&patch_lock);
		return sample_patch->first_sample;
	}

	return_sample = sample_patch->first_sample;
	last_sample = sample_patch->first_sample;
	while (last_sample != NULL) {
		if (freq > last_sample->freq_low) {
			if (freq < last_sample->freq_high) {
				WM_Unlock(&patch_lock);
				return last_sample;
			} else {
				return_sample = last_sample;
			}
		}
		last_sample = last_sample->next;
	}
	WM_Unlock(&patch_lock);
	return return_sample;
}

unsigned long int
read_var_length (struct _mdi *mdi, struct _miditrack *track) {
	unsigned long int var_data = 0;
	if (mdi->data[track->ptr] > 0x7f) {
		while(mdi->data[track->ptr] > 0x7f) {
			var_data |= mdi->data[track->ptr] & 0x7f;
			track->ptr++;
			if (track->ptr > mdi->size) {
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
				return 0xFFFFFFFF;
			}
			var_data = (var_data << 7);
		}
	}
	var_data |= mdi->data[track->ptr] & 0x7f;
	track->ptr++;

	if (track->ptr > mdi->size) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
		return 0xFFFFFFFF;
	}

	return var_data;
}

void
do_note_off (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	struct _note *nte;

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	nte = &mdi->note_table[0][ch][mdi->data[ptr]];
	if (!nte->active)
		nte = &mdi->note_table[1][ch][mdi->data[ptr]];
	if (!nte->active) {
		return;
	}

	if ((ch == 9) && (!(nte->modes & SAMPLE_LOOP))) {
		return;
	}

	if (nte->hold) {
		nte->hold |= HOLD_OFF;
	} else {
#if 0
		if (nte->modes & SAMPLE_SUSTAIN) {
			nte->env = 3;
			if (nte->env_level > nte->sample->env_target[3]) {
				nte->env_inc = -nte->sample->env_rate[3];
			} else {
				nte->env_inc = nte->sample->env_rate[3];
			}
		} else
#endif
		{
			if (nte->env < 4) {
				nte->env = 4;
				if (nte->env_level > nte->sample->env_target[4]) {
					nte->env_inc = -nte->sample->env_rate[4];
				} else {
					nte->env_inc = nte->sample->env_rate[4];
				}
			}
		}
	}
	return;
}

inline unsigned long int
get_inc (struct _mdi *mdi, struct _note *nte) {
	int ch = nte->noteid >> 8;
	signed long int note_f;
	unsigned long int freq;

	if (__builtin_expect((nte->patch->note != 0),0)) {
		note_f = nte->patch->note * 100;
	} else {
		note_f = (nte->noteid & 0x7f) * 100;
	}
	note_f += mdi->channel[ch].pitch_adjust;
	if (__builtin_expect((note_f < 0), 0)) {
		note_f = 0;
	} else if (__builtin_expect((note_f > 12700), 0)) {
		note_f = 12700;
	}
	freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200));
	return (((freq / ((WM_SampleRate * 100) / 1024)) * 1024 / nte->sample->inc_div));
}

inline signed short int
get_volume(struct _mdi *mdi, unsigned char ch, struct _note *nte) {
	signed long int volume;

 	if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) {
		volume = (lin_volume[mdi->channel[ch].volume] *
			lin_volume[mdi->channel[ch].expression] *
			lin_volume[nte->velocity]) / 1048576;
	} else {
		volume = (sqr_volume[mdi->channel[ch].volume] *
			sqr_volume[mdi->channel[ch].expression] *
			sqr_volume[nte->velocity]) / 1048576;
	}
	return ((volume * nte->sample->peek_adjust) >> 10);
}

void
do_note_on (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	struct _note *nte;
	unsigned long int freq = 0;
	struct _patch *patch;
	struct _sample *sample;

	if (mdi->data[ptr+1] == 0x00) {
		do_note_off(ch, mdi, ptr);
		return;
	}

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	if (ch != 9) {
		patch = mdi->channel[ch].patch;
		if (patch == NULL) {
			return;
		}
		freq = freq_table[(mdi->data[ptr] % 12) * 100] >> (10 -(mdi->data[ptr] / 12));
	} else {
		patch = get_patch_data(mdi, ((mdi->channel[ch].bank << 8) | mdi->data[ptr] | 0x80));
		if (patch == NULL) {
			return;
		}
		if (patch->note) {
			freq = freq_table[(patch->note % 12) * 100] >> (10 -(patch->note / 12));
		} else {
			freq = freq_table[(mdi->data[ptr] % 12) * 100] >> (10 -(mdi->data[ptr] / 12));
		}
	}

	sample = get_sample_data(patch, (freq / 100));

	if (sample == NULL) {
		return;
	}

	nte = &mdi->note_table[0][ch][mdi->data[ptr]];

	if (nte->active) {
		if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) && (!(nte->hold & HOLD_OFF)))
			return;
		nte->next = &mdi->note_table[1][ch][mdi->data[ptr]];
		nte->env = 6;
		nte->env_inc = -nte->sample->env_rate[6];
		nte = &mdi->note_table[1][ch][mdi->data[ptr]];
	} else {
		if (mdi->note_table[1][ch][mdi->data[ptr]].active) {
			if ((nte->modes & SAMPLE_ENVELOPE) && (nte->env < 3) && (!(nte->hold & HOLD_OFF)))
				return;
			mdi->note_table[1][ch][mdi->data[ptr]].next = nte;
			mdi->note_table[1][ch][mdi->data[ptr]].env = 6;
			mdi->note_table[1][ch][mdi->data[ptr]].env_inc = -mdi->note_table[1][ch][mdi->data[ptr]].sample->env_rate[6];
		} else {
			*mdi->last_note = nte;
			mdi->last_note++;
			nte->active = 1;
		}
	}
	nte->noteid = (ch << 8) | mdi->data[ptr];
	nte->patch = patch;
	nte->sample = sample;
	nte->sample_pos = 0;
	nte->sample_inc = get_inc (mdi, nte);
	nte->velocity = mdi->data[ptr+1];
	nte->env = 0;
	nte->env_inc = nte->sample->env_rate[0];
	nte->env_level = 0;
	nte->modes = sample->modes;
	nte->hold = mdi->channel[ch].hold;
	nte->vol_lvl = get_volume(mdi, ch, nte);
	nte->next = NULL;
}

void
do_aftertouch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	struct _note *nte;

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	nte = &mdi->note_table[0][ch][mdi->data[ptr]];
	if (!nte->active) {
		nte = &mdi->note_table[1][ch][mdi->data[ptr]];
		if (!nte->active) {
			return;
		}
	}

	nte->velocity = mdi->data[ptr+1];
	nte->vol_lvl = get_volume(mdi, ch, nte);

	if (nte->next) {
		nte->next->velocity = mdi->data[ptr+1];
		nte->next->vol_lvl = get_volume(mdi, ch, nte->next);
	}
}


void
do_pan_adjust (struct _mdi *mdi, unsigned char ch) {
	signed short int pan_adjust = mdi->channel[ch].balance + mdi->channel[ch].pan;
	signed long int left, right;

	if (pan_adjust > 63) {
		pan_adjust = 63;
	} else if (pan_adjust < -64) {
		pan_adjust = -64;
	}

	pan_adjust += 64;

	if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) {
		left = (lin_volume[127 - pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576;
		right= (lin_volume[pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576;
	} else {
		left =  (pan_volume[127 - pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576;
		right = (pan_volume[pan_adjust] * WM_MasterVolume * mdi->amp) / 1048576;
	}

	mdi->channel[ch].left_adjust = left;
	mdi->channel[ch].right_adjust = right;
}

void
do_control (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	struct _note **note_data = mdi->note;

	switch (mdi->data[ptr]) {
		case 0: // Bank Select
			mdi->channel[ch].bank = mdi->data[ptr+1];
			break;
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
			break;
		case 6: // Data Entry Course
			if (mdi->channel[ch].reg_data == 0x0000) { // Pitch Bend Range
				int data_tmp;
				data_tmp = mdi->channel[ch].pitch_range % 100;
				mdi->channel[ch].pitch_range = mdi->data[ptr+1] * 100 + data_tmp;
			}
			break;
		case 7: // Channel Volume
			mdi->channel[ch].volume = mdi->data[ptr+1];

			if (note_data != mdi->last_note) {
				do {
					if (((*note_data)->noteid >> 8) == ch) {
						(*note_data)->vol_lvl = get_volume(mdi, ch, *note_data);
						if ((*note_data)->next)
							(*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next);
					}
					note_data++;
				} while (note_data != mdi->last_note);
			}
			break;
		case 8: // Channel Balance
			mdi->channel[ch].balance = mdi->data[ptr+1] - 64;
			do_pan_adjust(mdi, ch);
			break;
		case 9:
			break;
		case 10: // Channel Pan
			mdi->channel[ch].pan = mdi->data[ptr+1] - 64;
			do_pan_adjust(mdi, ch);
			break;
		case 11: // Channel Expression
			mdi->channel[ch].expression = mdi->data[ptr+1];

			if (note_data != mdi->last_note) {
				do {
					if (((*note_data)->noteid >> 8) == ch) {
						(*note_data)->vol_lvl = get_volume(mdi, ch, *note_data);
						if ((*note_data)->next)
							(*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next);
					}
					note_data++;
				} while (note_data != mdi->last_note);
			}
			break;
		case 12:
		case 13:
		case 14:
		case 15:
		case 16:
		case 17:
		case 18:
		case 19:
		case 20:
		case 21:
		case 22:
		case 23:
		case 24:
		case 25:
		case 26:
		case 27:
		case 28:
		case 29:
		case 30:
		case 31:
		case 32:
		case 33:
		case 34:
		case 35:
		case 36:
		case 37:
			break;
		case 38: // Data Entry Fine
			if (mdi->channel[ch].reg_data == 0x0000) { // Pitch Bend Range
				int data_tmp;
				data_tmp = mdi->channel[ch].pitch_range / 100;
				mdi->channel[ch].pitch_range = (data_tmp * 100) + mdi->data[ptr+1];
			}
			break;
		case 39:
		case 40:
		case 41:
		case 42:
		case 43:
			printf("\rController %i used\n",mdi->data[ptr]);
			break;
		case 44:
		case 45:
		case 46:
		case 47:
		case 48:
		case 49:
		case 50:
		case 51:
		case 52:
		case 53:
		case 54:
		case 55:
		case 56:
		case 57:
		case 58:
		case 59:
		case 60:
		case 61:
		case 62:
		case 63:
			break;
		case 64: // Channel Hold
			if (mdi->data[ptr+1] > 63) {
				mdi->channel[ch].hold = 1;
			} else {
				mdi->channel[ch].hold = 0;
				if (note_data != mdi->last_note) {
					do {
						if (((*note_data)->noteid >> 8) == ch) {
							if ((*note_data)->hold & HOLD_OFF) {
								if ((*note_data)->modes & SAMPLE_ENVELOPE) {
#if 0
									if ((*note_data)->modes & SAMPLE_SUSTAIN) {
										(*note_data)->env = 3;
										if ((*note_data)->env_level > (*note_data)->sample->env_target[3]) {
											(*note_data)->env_inc = -(*note_data)->sample->env_rate[3];
										} else {
											(*note_data)->env_inc = (*note_data)->sample->env_rate[3];
										}
									} else
#endif
									{
										if ((*note_data)->env < 4) {
											(*note_data)->env = 4;
											if ((*note_data)->env_level > (*note_data)->sample->env_target[4]) {
												(*note_data)->env_inc = -(*note_data)->sample->env_rate[4];
											} else {
												(*note_data)->env_inc = (*note_data)->sample->env_rate[4];
											}
										}
									}
								}
							}
							(*note_data)->hold = 0x00;
						}
						note_data++;
					} while (note_data != mdi->last_note);
				}
			}
			break;
		case 65:
		case 66:
		case 67:
		case 68:
		case 69:
		case 70:
		case 71:
		case 72:
		case 73:
		case 74:
		case 75:
		case 76:
		case 77:
		case 78:
		case 79:
		case 80:
		case 81:
		case 82:
		case 83:
		case 84:
		case 85:
		case 86:
		case 87:
		case 88:
		case 89:
		case 90:
		case 91:
		case 92:
		case 93:
		case 94:
		case 95:
		case 96:
		case 97:
		case 98:
		case 99:
			break;
		case 100: // Registered Param Fine
			mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0xFF00) | mdi->data[ptr+1];
			break;
		case 101: // Registered Param Course
			mdi->channel[ch].reg_data = (mdi->channel[ch].reg_data & 0xFF) | (mdi->data[ptr+1] << 8);
			break;
		case 102:
		case 103:
		case 104:
		case 105:
		case 106:
		case 107:
		case 108:
		case 109:
		case 110:
		case 111:
		case 112:
		case 113:
		case 114:
		case 115:
		case 116:
		case 117:
		case 118:
		case 119:
			break;
		case 120: // All Channel Sound Off
			if (note_data != mdi->last_note) {
				do {
					if (((*note_data)->noteid >> 8) == ch) {
						(*note_data)->active = 0;
						if ((*note_data)->next) {
							(*note_data)->next = NULL;
						}
					}
					note_data++;
				} while (note_data != mdi->last_note);
				mdi->last_note = mdi->note;
			}
			break;
		case 121: // All Controlers Off
			mdi->channel[ch].expression = 127;
			mdi->channel[ch].pressure = 0;
			mdi->channel[ch].volume = 100;
			mdi->channel[ch].pan = 0;
			mdi->channel[ch].balance = 0;
			mdi->channel[ch].reg_data = 0xffff;
			mdi->channel[ch].pitch_range = 200;
			mdi->channel[ch].pitch = 0;
			mdi->channel[ch].pitch_adjust = 0;
			mdi->channel[ch].hold = 0;
			do_pan_adjust(mdi, ch);

			if (note_data != mdi->last_note) {
				do {
					if (((*note_data)->noteid >> 8 ) == ch) {
						(*note_data)->sample_inc = get_inc (mdi, *note_data);
						(*note_data)->velocity = 0;
						(*note_data)->vol_lvl = get_volume(mdi, ch, *note_data);
						(*note_data)->hold = 0;

						if ((*note_data)->next) {
							(*note_data)->next->velocity = mdi->data[ptr];
							(*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next);
						}

					}
					note_data++;
				} while (note_data != mdi->last_note);
			}
			break;
		case 122:
			break;
		case 123: // All Channel Notes Off
			if (ch == 9)
				return;
			if (note_data != mdi->last_note) {
				do {
					if (((*note_data)->noteid >> 8) == ch) {
						if (!(*note_data)->hold){
							if ((*note_data)->modes & SAMPLE_ENVELOPE) {
								if ((*note_data)->env < 5) {
									if ((*note_data)->env_level > (*note_data)->sample->env_target[5]) {
										(*note_data)->env_inc = -(*note_data)->sample->env_rate[5];
									} else {
										(*note_data)->env_inc = (*note_data)->sample->env_rate[5];
									}
									(*note_data)->env = 5;
								}
							}
						} else {
							(*note_data)->hold |= HOLD_OFF;
						}
					}
					note_data++;
				} while (note_data != mdi->last_note);
			}
			break;
		case 124:
		case 125:
		case 126:
		case 127:
			break;
	}
}

void
do_patch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	if (ch != 9) {
		mdi->channel[ch].patch = get_patch_data(mdi, ((mdi->channel[ch].bank << 8) | mdi->data[ptr]));
	} else {
		mdi->channel[ch].bank = mdi->data[ptr];
	}
}

void
do_channel_pressure (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	struct _note **note_data = mdi->note;
	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	if (note_data != mdi->last_note) {
		do {
			if (((*note_data)->noteid >> 8 ) == ch) {
				(*note_data)->velocity = mdi->data[ptr];
				(*note_data)->vol_lvl = get_volume(mdi, ch, *note_data);

				if ((*note_data)->next) {
					(*note_data)->next->velocity = mdi->data[ptr];
					(*note_data)->next->vol_lvl = get_volume(mdi, ch, (*note_data)->next);
				}
			}
			note_data++;
		} while (note_data != mdi->last_note);
	}
}

void
do_pitch (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	struct _note **note_data = mdi->note;

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	mdi->channel[ch].pitch = ((mdi->data[ptr+1] << 7) | mdi->data[ptr]) - 0x2000;

	if (mdi->channel[ch].pitch < 0) {
		mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range * mdi->channel[ch].pitch / 8192;
	} else {
		mdi->channel[ch].pitch_adjust = mdi->channel[ch].pitch_range * mdi->channel[ch].pitch / 8191;
	}

	if (note_data != mdi->last_note) {
		do {
			if (((*note_data)->noteid >> 8 ) == ch) {
				(*note_data)->sample_inc = get_inc (mdi, *note_data);
			}
			note_data++;
		} while (note_data != mdi->last_note);
	}
}

void
do_message (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	unsigned char event_type = 0xF0 | ch;
	static unsigned long int tempo = 500000;

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	if (event_type == 0xFF) {
		if ((mdi->data[ptr] == 0x51) && (mdi->data[ptr+1] == 3)) {
			tempo = (mdi->data[ptr+2] << 16) | (mdi->data[ptr+3] << 8) | mdi->data[ptr+4];
			if (tempo == 0)
				mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions);
			else
				mdi->samples_per_delta = (WM_SampleRate << 10) / ((1000000 * mdi->divisions) / tempo);
		}
	}
}


void
do_null (unsigned char ch, struct _mdi *mdi, unsigned long int ptr) {
	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
};


void
WM_ResetToStart(midi * handle) {
	struct _mdi *mdi = (struct _mdi *)handle;
	int i;

	mdi->index_count = 0;
	mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions);
	mdi->samples_to_mix = 0;
	mdi->info.current_sample= 0;

	for (i=0; i<16; i++) {
		mdi->channel[i].bank = 0;
		mdi->channel[i].patch = NULL;
		mdi->channel[i].hold = 0;
		mdi->channel[i].volume = 100;
		mdi->channel[i].pressure = 127;
		mdi->channel[i].expression = 127;
		mdi->channel[i].balance = 0;
		mdi->channel[i].pan = 0;
		mdi->channel[i].left_adjust = 1.0;
		mdi->channel[i].right_adjust = 1.0;
		mdi->channel[i].pitch = 0;
		mdi->channel[i].pitch_range = 200;
		mdi->channel[i].reg_data = 0xFFFF;
	}
}

void
WM_RecalcSamples (midi * handle) {
	struct _mdi *mdi = (struct _mdi *)handle;
	struct _note **note_data = mdi->note;
	unsigned long int total_samples = 0;
	unsigned long int count_a;
	unsigned long int count_b;
	unsigned long int env_level;

	if (note_data != mdi->last_note) {
		do {
			env_level = (*note_data)->env_level;
			count_a = 0;
			count_b = 0;
			if ((*note_data)->env < 4) {
				if (env_level > (*note_data)->sample->env_target[3]) {
					count_a += (env_level - (*note_data)->sample->env_target[3] + (*note_data)->sample->env_rate[3] - 1) / (*note_data)->sample->env_rate[3];
				} else {
					count_a += ((*note_data)->sample->env_target[3] - env_level + (*note_data)->sample->env_rate[3] - 1) / (*note_data)->sample->env_rate[3];
				}
				env_level = (*note_data)->sample->env_target[3];
			}
			if ((*note_data)->env < 5) {
				if (env_level > (*note_data)->sample->env_target[4]) {
					count_a += (env_level - (*note_data)->sample->env_target[4] + (*note_data)->sample->env_rate[4] - 1) / (*note_data)->sample->env_rate[4];
				} else {
					count_a += ((*note_data)->sample->env_target[4] - env_level + (*note_data)->sample->env_rate[4] - 1) / (*note_data)->sample->env_rate[4];
				}
				env_level = (*note_data)->sample->env_target[4];
			}
			if ((*note_data)->env < 6) {
				if (env_level > (*note_data)->sample->env_target[5]) {
					count_a += (env_level - (*note_data)->sample->env_target[5] + (*note_data)->sample->env_rate[5] - 1) / (*note_data)->sample->env_rate[5];
				} else {
					count_a += ((*note_data)->sample->env_target[5] - env_level + (*note_data)->sample->env_rate[5] - 1) / (*note_data)->sample->env_rate[5];
				}
				env_level = (*note_data)->sample->env_target[5];
			}
			if ((*note_data)->env == 6) {
				count_a = (env_level + (*note_data)->sample->env_rate[6] - 1) / (*note_data)->sample->env_rate[6];
				env_level = (*note_data)->sample->env_target[6];
			}
			if (env_level != 0) {
				if ((*note_data)->modes & SAMPLE_LOOP) {
					unsigned long int smpl_pos = (*note_data)->sample_pos + (count_a * (*note_data)->sample_inc);
					if (smpl_pos > ((*note_data)->sample->loop_end << 10)) {
						while (smpl_pos > ((*note_data)->sample->loop_end << 10)) {
							smpl_pos -= ((*note_data)->sample->loop_end - (*note_data)->sample->loop_start) << 10;
						}
						count_a += (((*note_data)->sample->data_length << 10) - smpl_pos + (*note_data)->sample_inc - 1) / (*note_data)->sample_inc;
					}
					} else {
					count_b = (((*note_data)->sample->data_length << 10) - (*note_data)->sample_pos + (*note_data)->sample_inc - 1) / (*note_data)->sample_inc;
				}
				if (count_b != 0) {
					if (count_b < count_a) {
						if (total_samples < count_b)
							 total_samples = count_b;
					} else {
						if (total_samples < count_a)
							total_samples = count_a;
					}
				} else {
					if (total_samples < count_a)
							total_samples = count_a;
				}
			} else {
				if (!((*note_data)->modes & SAMPLE_LOOP)) {
					count_b = (((*note_data)->sample->data_length << 10) - (*note_data)->sample_pos) / (*note_data)->sample_inc;
					if (count_b < count_a) {
						if (total_samples < count_b)
							total_samples = count_b;
					} else {
						if (total_samples < count_a)
							total_samples = count_a;
						}
				} else {
					if (total_samples < count_a)
						total_samples = count_a;
				}
			}
		note_data++;
		} while (note_data != mdi->last_note);
	}
	mdi->info.approx_total_samples += total_samples;
	mdi->recalc_samples = 0;
}

void
do_amp_setup_note_off (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	mdi->lin_cur_vol -=  (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
		lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
	mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
		log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

	mdi->note_vel[ch][mdi->data[track->ptr]] = 0;

	track->running_event = 0x80 | ch;
	track->ptr += 2;
	return;
}

void
do_amp_setup_note_on (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	if (mdi->data[track->ptr+1] == 0x00) {
		do_amp_setup_note_off(ch, mdi, track);
		track->running_event = 0x90 | ch;
		return;
	}

	if (mdi->note_vel[ch][mdi->data[track->ptr]]) {
		mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
			lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
		mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
			log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;
	}

	mdi->note_vel[ch][mdi->data[track->ptr]] = mdi->data[track->ptr+1];

	mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
		lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
	mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
		log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

	if (mdi->lin_cur_vol > mdi->lin_max_vol) {
		mdi->lin_max_vol = mdi->lin_cur_vol;
	}
	if (mdi->log_cur_vol > mdi->log_max_vol) {
		mdi->log_max_vol = mdi->log_cur_vol;
	}
	if (ch == 9) {
		load_patch(mdi, ((mdi->channel[ch].bank << 8) | (mdi->data[track->ptr] | 0x80)));
	}

	track->running_event = 0x90 | ch;
	track->ptr += 2;
	return;
}

void
do_amp_setup_aftertouch (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	unsigned char pres = mdi->data[track->ptr+1];

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	if (pres == 0)
		pres = 1;

	if (mdi->note_vel[ch][mdi->data[track->ptr]] != 0) {
		mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
			lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
		mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
			log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

		mdi->note_vel[ch][mdi->data[track->ptr]] = pres;

		mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
			lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
		mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][mdi->data[track->ptr]]] *
			log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

		if (mdi->lin_cur_vol > mdi->lin_max_vol) {
			mdi->lin_max_vol = mdi->lin_cur_vol;
		}
		if (mdi->log_cur_vol > mdi->log_max_vol) {
			mdi->log_max_vol = mdi->log_cur_vol;
		}
	}
	track->running_event = 0xA0 | ch;
	track->ptr += 2;
	return;
}


void
do_amp_setup_control (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	int i;

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	if (mdi->data[track->ptr] == 0x00) {
		mdi->channel[ch].bank = mdi->data[track->ptr + 1];
	} else if (mdi->data[track->ptr] == 0x07) {
		for (i=0; i < 128; i++) {
			if (mdi->note_vel[ch][i] == 0)
				continue;
			mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][i]] *
				lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
			mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][i]] *
				log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

			mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][i]] *
				lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->data[track->ptr + 1]]) / 1048576;
			mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][i]] *
				log_volume[mdi->ch_exp[ch]] * log_volume[mdi->data[track->ptr + 1]]) / 1048576;

		}
		mdi->ch_vol[ch] = mdi->data[track->ptr + 1];
		if (mdi->lin_cur_vol > mdi->lin_max_vol) {
			mdi->lin_max_vol = mdi->lin_cur_vol;
		}
		if (mdi->log_cur_vol > mdi->log_max_vol) {
			mdi->log_max_vol = mdi->log_cur_vol;
		}
	} else if (mdi->data[track->ptr] == 0x0B) {
		for (i=0; i < 128; i++) {
			if (mdi->note_vel[ch][i] == 0)
				continue;
			mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][i]] *
				lin_volume[mdi->ch_vol[ch]] * lin_volume[mdi->ch_exp[ch]]) / 1048576;
			mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][i]] *
				log_volume[mdi->ch_vol[ch]] * log_volume[mdi->ch_exp[ch]]) / 1048576;

			mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][i]] *
				lin_volume[mdi->ch_vol[ch]] * lin_volume[mdi->data[track->ptr + 1]]) / 1048576;
			mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][i]] *
				log_volume[mdi->ch_vol[ch]] * log_volume[mdi->data[track->ptr + 1]]) / 1048576;

		}
		mdi->ch_exp[ch] = mdi->data[track->ptr + 1];
		if (mdi->lin_cur_vol > mdi->lin_max_vol) {
			mdi->lin_max_vol = mdi->lin_cur_vol;
		}
		if (mdi->log_cur_vol > mdi->log_max_vol) {
			mdi->log_max_vol = mdi->log_cur_vol;
		}
	}

	track->running_event = 0xB0 | ch;
	track->ptr += 2;
	return;
}

void
do_amp_setup_patch (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	if (ch == 9) {
		mdi->channel[ch].bank = mdi->data[track->ptr];
	} else {
		load_patch(mdi, ((mdi->channel[ch].bank << 8) | mdi->data[track->ptr]));
	}
	track->running_event = 0xC0 | ch;
	track->ptr++;
	return;
}

void
do_amp_setup_channel_pressure (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	int i;
	unsigned char pres = mdi->data[track->ptr];

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);

	if (pres == 0)
		pres = 1;

	for (i=0; i < 128; i++) {
		if (mdi->note_vel[ch][i] == 0)
			continue;
		mdi->lin_cur_vol -= (lin_volume[mdi->note_vel[ch][i]] *
			lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
		mdi->log_cur_vol -= (sqr_volume[mdi->note_vel[ch][i]] *
			log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

		mdi->note_vel[ch][i] = pres;

		mdi->lin_cur_vol += (lin_volume[mdi->note_vel[ch][i]] *
			lin_volume[mdi->ch_exp[ch]] * lin_volume[mdi->ch_vol[ch]]) / 1048576;
		mdi->log_cur_vol += (sqr_volume[mdi->note_vel[ch][i]] *
			log_volume[mdi->ch_exp[ch]] * log_volume[mdi->ch_vol[ch]]) / 1048576;

	}
	if (mdi->lin_cur_vol > mdi->lin_max_vol) {
		mdi->lin_max_vol = mdi->lin_cur_vol;
	}
	if (mdi->log_cur_vol > mdi->log_max_vol) {
		mdi->log_max_vol = mdi->log_cur_vol;
	}
	track->running_event = 0xD0 | ch;
	track->ptr++;
	return;
}

void
do_amp_setup_pitch (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	track->running_event = 0xE0 | ch;
	track->ptr += 2;
	return;
}

void
do_amp_setup_message (unsigned char ch, struct _mdi *mdi, struct _miditrack *track) {
	unsigned long int event_length;
	unsigned char event_type = 0xF0 | ch;
	unsigned char event_data = mdi->data[track->ptr];
	static unsigned long int tempo = 500000;

	MIDI_EVENT_DEBUG(__FUNCTION__,ch);
	if (event_type == 0xF0) {
		track->running_event = 0x00;
		do {
			track->ptr++;
		} while ( mdi->data[track->ptr] != 0xF7);
		track->ptr++;
	} else {
		track->ptr += 1;
		event_length = read_var_length(mdi, track);
		if (event_length == 0xFFFFFFFF) {
			track->delta = 0xFFFFFFFF;
			return;
		}
		if (event_type == 0xFF) {
			if ((event_data == 0x2F) && (event_length == 0)) { // Track End
				track->EOT = 1;
				return;
			} else if ((event_data == 0x51) && (event_length == 3)) {  // Tempo Change
				tempo = (mdi->data[track->ptr] << 16) | (mdi->data[track->ptr+1] << 8) | mdi->data[track->ptr+2];
				if (tempo == 0)
					mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions);
				else
					mdi->samples_per_delta = (WM_SampleRate << 10) / ((1000000 * mdi->divisions) / tempo);
			}
		}
		track->ptr += event_length;
	}
}


struct _mdi *
WM_ParseNewMidi(unsigned char *mididata, unsigned long int midisize ) {
	int i;
	unsigned char eot[] = { 0xff, 0x2f, 0x00};
	unsigned long int index_count = 0;
	unsigned long int temp_delta = 0xffffff00;
	struct _mdi *mdi = NULL;
	void (*do_event[])(unsigned char ch, struct _mdi *midifile, struct _miditrack *track) = {
		*do_amp_setup_note_off,
		*do_amp_setup_note_on,
		*do_amp_setup_aftertouch,
		*do_amp_setup_control,
		*do_amp_setup_patch,
		*do_amp_setup_channel_pressure,
		*do_amp_setup_pitch,
		*do_amp_setup_message
	};
	unsigned long int midiofs = 0;
	unsigned long int midi_track_counter = 0;
	unsigned long int last_delta;
	unsigned long int minus_delta = 0;
	unsigned char current_event = 0;
	unsigned short int no_tracks;
	unsigned long int EOT_count = 0;
	struct _miditrack *tmp_trackdata;
	struct _hndl *tmp_handle = NULL;

	mdi = malloc(sizeof(struct _mdi));
	if (mdi == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno);
		free (mididata);
		return NULL;
	}

	// Initialize data
	memset(mdi, 0, sizeof(struct _mdi));
	mdi->lock = 0;
	mdi->data = mididata;
	mdi->size = midisize;
	mdi->info.mixer_options = WM_MixerOptions;

	mdi->index = malloc(((midisize / 2) + 1)* sizeof(struct _mdi_index));
	if (mdi->index == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno);
		free(mdi);
		return NULL;
	}

	load_patch(mdi, 0x0000);

	for (i=0; i<16; i++) {
		mdi->channel[i].volume = 100;
		mdi->channel[i].pressure = 127;
		mdi->channel[i].expression = 127;
		mdi->channel[i].pitch_range = 200;
		mdi->channel[i].reg_data = 0xFFFF;
		mdi->ch_vol[i] = 100;
		mdi->ch_exp[i] = 127;
		mdi->channel[i].patch = get_patch_data(mdi, 0x0000);
	}

	midiofs = 0;
	if (strncmp(mididata,"RIFF",4) == 0)
		midiofs = 20;

	if (strncmp(&mididata[midiofs],"MThd",4) != 0) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID,"(not a midi file)", 0);
		free(mdi->index);
		free(mdi);
		return NULL;
	}

	if ((midiofs + 25) > midisize) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT,"(too short)", 0);
		free(mdi->index);
		free(mdi);
		return NULL;
	}

	midiofs += 9;
	if (mididata[midiofs] > 1) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0);
		free(mdi->index);
		free(mdi);
		return NULL;
	}
	midiofs++;

	no_tracks = mididata[midiofs] << 8 | mididata[midiofs+1];
	midiofs += 2;

	mdi->divisions = mididata[midiofs] << 8 | mididata[midiofs+1];
	mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions);
	midiofs += 2;

	tmp_trackdata = calloc(no_tracks, sizeof(struct _miditrack));

	if (first_handle == NULL) {
		first_handle = malloc(sizeof(struct _hndl));
		if (first_handle == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno);
			free(mdi->data);
			free(mdi);
			return NULL;
		}
		first_handle->handle = (void *)mdi;
		first_handle->prev = NULL;
		first_handle->next = NULL;
	} else {
		tmp_handle = first_handle;
			free(tmp_trackdata);
		if (tmp_handle->next != NULL) {
			while (tmp_handle->next != NULL)
				tmp_handle = tmp_handle->next;
		}
		tmp_handle->next = malloc(sizeof(struct _hndl));
		if (tmp_handle->next == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM," to parse midi data", errno);
			free(mdi->data);
			free(mdi);
			return NULL;
		}
		tmp_handle->next->prev = tmp_handle;
		tmp_handle = tmp_handle->next;
		tmp_handle->next = NULL;
		tmp_handle->handle = (void *)mdi;
	}


	// grab track offsets;

	midi_track_counter = 0;
	while (midi_track_counter != no_tracks) {
		if ((midiofs + 12) > midisize) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
			WildMidi_Close(mdi);
			free(tmp_trackdata);
			return NULL;
		}
		if (strncmp(&mididata[midiofs],"MTrk",4) != 0) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(Expected track header)", 0);
			WildMidi_Close(mdi);
			free(tmp_trackdata);
			return NULL;
		}
		midiofs += 4;
		tmp_trackdata[midi_track_counter].length = mididata[midiofs] << 24 | mididata[midiofs+1] << 16 | mididata[midiofs+2] << 8 | mididata[midiofs+3];
		midiofs += 4;

		if (midisize < (midiofs + tmp_trackdata[midi_track_counter].length)) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0);
			WildMidi_Close(mdi);
			free(tmp_trackdata);
			return NULL;
		}
		tmp_trackdata[midi_track_counter].ptr = midiofs;
		tmp_trackdata[midi_track_counter].EOT = 0;
		tmp_trackdata[midi_track_counter].running_event = 0;
		tmp_trackdata[midi_track_counter].delta = read_var_length(mdi, &tmp_trackdata[midi_track_counter]);
		if (tmp_trackdata[midi_track_counter].delta == 0xFFFFFFFF) {
			WildMidi_Close(mdi);
			free(tmp_trackdata);
			return NULL;
		}
		midiofs += tmp_trackdata[midi_track_counter].length;

		if (memcmp(&mididata[midiofs-3], eot,3) != 0) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(Expected EOT)", 0);
			WildMidi_Close(mdi);
			free(tmp_trackdata);
			return NULL;
		}
		if (tmp_trackdata[midi_track_counter].delta < temp_delta) {
			temp_delta = tmp_trackdata[midi_track_counter].delta;
		}
		midi_track_counter++;
	}

// set midi info
	mdi->index[0].offset = 0;
	mdi->index[0].delta = temp_delta;

	while (EOT_count != no_tracks) {
		last_delta = 0;
		for (i = 0; i < no_tracks; i++) {
			if (tmp_trackdata[i].EOT) {
				continue;
			}
			if (tmp_trackdata[i].delta) {
				tmp_trackdata[i].delta -= minus_delta;
				if (tmp_trackdata[i].delta) {
					if ((last_delta == 0) || (last_delta > tmp_trackdata[i].delta)) {
						last_delta = tmp_trackdata[i].delta;
					}
					continue;
				}
			}
			do {
				if (mdi->data[tmp_trackdata[i].ptr] < 0x80) {
					current_event = tmp_trackdata[i].running_event;
					if (current_event < 0x80) {
						WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(expected event)", 0);
						WildMidi_Close(mdi);
						free(tmp_trackdata);
						return NULL;
					}
				} else {
					current_event = mdi->data[tmp_trackdata[i].ptr];
					tmp_trackdata[i].ptr++;
				}


				index_count++;
				mdi->index[index_count].offset = tmp_trackdata[i].ptr;
				mdi->index[index_count].delta = 0;
				mdi->index[index_count].event = current_event;

				do_event[((current_event & 0xF0) >> 4) - 8]((current_event & 0x0F), mdi, &tmp_trackdata[i]);
				if (tmp_trackdata[i].delta == 0xFFFFFFFF) {
					WildMidi_Close(mdi);
					free(tmp_trackdata);
					return NULL;
				}
				if (tmp_trackdata[i].EOT) {
					EOT_count++;
					break;
				}
				tmp_trackdata[i].delta = read_var_length(mdi, &tmp_trackdata[i]);
				if (tmp_trackdata[i].delta == 0xFFFFFFFF) {
					WildMidi_Close(mdi);
					free(tmp_trackdata);
					return NULL;
				}
			} while (!(tmp_trackdata[i].delta));
			if ((last_delta == 0) || (last_delta > tmp_trackdata[i].delta)) {
				if (tmp_trackdata[i].delta != 0) {
					last_delta = tmp_trackdata[i].delta;
				}
			}
		}
//		printf("\rLast Delta %lu\n",last_delta);
		mdi->index[index_count].delta = last_delta;
		mdi->samples_to_mix += last_delta * mdi->samples_per_delta;
		mdi->sample_count += mdi->samples_to_mix >> 10;
		mdi->samples_to_mix %= 1024;
		minus_delta = last_delta;
	}
	mdi->sample_count -= (mdi->index[index_count - 1].delta * mdi->samples_per_delta) >> 10;
	mdi->index[index_count - 1].delta = 0;
	mdi->index_size = index_count;
	mdi->index = realloc(mdi->index, (sizeof(struct _mdi_index) * mdi->index_size));
	if (mdi->index == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno);
		WildMidi_Close(mdi);
		free(tmp_trackdata);
		return NULL;
	}
	mdi->info.approx_total_samples = mdi->sample_count + 1;
	mdi->samples_to_mix = 0;
	mdi->sample_count = 0;
	mdi->info.current_sample = 0;
	mdi->samples_per_delta = (WM_SampleRate << 10) / (2 * mdi->divisions);
	mdi->recalc_samples = 1;
	mdi->last_note = mdi->note;
	if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) {
		mdi->amp = 281;
	} else {
		mdi->amp = 281 * mdi->lin_max_vol / mdi->log_max_vol;
	}

	for (i = 0; i < 16; i++) {
		mdi->channel[i].bank = 0;
		do_pan_adjust(mdi, i);
	}

	for (i = 0; i < 4; i++) {
		mdi->filter.lowpass[i][0].in[0] = 0;
		mdi->filter.lowpass[i][0].in[1] = 0;
		mdi->filter.lowpass[i][1].in[0] = 0;
		mdi->filter.lowpass[i][1].in[1] = 0;

		mdi->filter.lowpass[i][0].out[0] = 0;
		mdi->filter.lowpass[i][0].out[1] = 0;
		mdi->filter.lowpass[i][1].out[0] = 0;
		mdi->filter.lowpass[i][1].out[1] = 0;

		mdi->filter.delay_pos[i][0] = 0;
		mdi->filter.delay_pos[i][1] = 0;

		mdi->filter.delay[i][0] = malloc(delay_size[i][0] * sizeof(signed long int));
		mdi->filter.delay[i][1] = malloc(delay_size[i][1] * sizeof(signed long int));
		memset (mdi->filter.delay[i][0], 0, (delay_size[i][0] * sizeof(signed long int)));
		memset (mdi->filter.delay[i][1], 0, (delay_size[i][1] * sizeof(signed long int)));

	}
	mdi->filter.in[0][0] = 0;
	mdi->filter.in[0][1] = 0;
	mdi->filter.in[1][0] = 0;
	mdi->filter.in[1][1] = 0;
	mdi->filter.out[0][0] = 0;
	mdi->filter.out[0][1] = 0;
	mdi->filter.out[1][0] = 0;
	mdi->filter.out[1][1] = 0;

	free(tmp_trackdata);
	return (mdi);
}

/*
 * =========================
 * External Functions
 * =========================
 */

const char *
WildMidi_GetString (unsigned short int info) {
	switch (info) {
		case WM_GS_VERSION:
			return WM_Version;
	}
	return NULL;
}

int
WildMidi_Init (const char * config_file, unsigned short int rate, unsigned short int options) {
	if (WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}

	if (config_file == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL config file pointer)", 0);
		return -1;
	}
	WM_InitPatches();
	if (WM_LoadConfig(config_file) == -1) {
		return -1;
	}

	if (options & 0xFFD8) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0);
		WM_FreePatches();
		return -1;
	}
	WM_MixerOptions = options;

	if ((rate < 11000) || (rate > 65000)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(rate out of bounds, range is 11000 - 65000)", 0);
		WM_FreePatches();
		return -1;
	}
	WM_SampleRate = rate;
	WM_Initialized = 1;
	patch_lock = 0;

	init_gauss();
	init_lowpass();
	return 0;
}

int
WildMidi_MasterVolume (unsigned char master_volume) {
	struct _mdi *mdi = NULL;
	struct _hndl * tmp_handle = first_handle;
	int i = 0;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (master_volume > 127) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(master volume out of range, range is 0-127)", 0);
		return -1;
	}

	WM_MasterVolume = lin_volume[master_volume];

	if (tmp_handle != NULL) {
		while(tmp_handle != NULL) {
			mdi = (struct _mdi *)tmp_handle->handle;
			for (i = 0; i < 16; i++) {
				do_pan_adjust(mdi, i);
			}
			tmp_handle = tmp_handle->next;
		}
	}

	return 0;
}

int
WildMidi_Close (midi * handle) {
	struct _mdi *mdi = (struct _mdi *)handle;
	struct _hndl * tmp_handle;
	struct _sample *tmp_sample;
	int i;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (handle == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	if (first_handle == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(no midi's open)", 0);
		return -1;
	}
	WM_Lock(&mdi->lock);
	if (first_handle->handle == handle) {
		tmp_handle = first_handle->next;
		free (first_handle);
		first_handle = tmp_handle;
		if (first_handle != NULL)
			first_handle->prev = NULL;
	} else {
		tmp_handle = first_handle;
		while (tmp_handle->handle != handle) {
			tmp_handle = tmp_handle->next;
			if (tmp_handle == NULL) {
				WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(handle does not exist)", 0);
				return -1;
			}
		}
		tmp_handle->prev->next = tmp_handle->next;
		if (tmp_handle->next != NULL) {
			tmp_handle->next->prev = tmp_handle->prev;
		}
		free (tmp_handle);
	}

	if (mdi->patch_count != 0) {
		WM_Lock(&patch_lock);
		for (i = 0; i < mdi->patch_count; i++) {
			mdi->patches[i]->inuse_count--;
			if (mdi->patches[i]->inuse_count == 0) {
				//free samples here
				if (mdi->patches[i]->first_sample != NULL) {
					while (mdi->patches[i]->first_sample != NULL) {
						tmp_sample = mdi->patches[i]->first_sample->next;
						if (mdi->patches[i]->first_sample->data)
							free(mdi->patches[i]->first_sample->data);
						free(mdi->patches[i]->first_sample);
						mdi->patches[i]->first_sample = tmp_sample;
					}
					mdi->patches[i]->loaded = 0;
				}
			}
		}
		WM_Unlock(&patch_lock);
		free (mdi->patches);
	}
	if (mdi->data != NULL) {
		free (mdi->data);
	}
	if (mdi->tmp_info != NULL) {
		free (mdi->tmp_info);
	}
	if (mdi->index != NULL)
		free (mdi->index);
	free (mdi);
	// no need to unlock cause the struct containing the lock no-longer exists;
	return 0;
}

midi *
WildMidi_Open (const char *midifile) {
	unsigned char *mididata = NULL;
	unsigned long int midisize = 0;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return NULL;
	}
	if (midifile == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL filename)", 0);
		return NULL;
	}

	if ((mididata = WM_BufferFile(midifile, &midisize)) == NULL) {
		return NULL;
	}

	return (void *)WM_ParseNewMidi(mididata,midisize);
}

midi *
WildMidi_OpenBuffer (unsigned char *midibuffer, unsigned long int size) {
	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return NULL;
	}
	if (midibuffer == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL midi data buffer)", 0);
		return NULL;
	}

	return (void *)WM_ParseNewMidi(midibuffer,size);
}

int
WildMidi_LoadSamples( midi * handle) {
	return 0;
}

int
WildMidi_FastSeek ( midi * handle, unsigned long int *sample_pos) {
	struct _mdi *mdi = (struct _mdi *)handle;
	struct _note **note_data = mdi->note;
	void (*do_event[])(unsigned char ch, struct _mdi *midifile, unsigned long int ptr) = {
		*do_null,
		*do_null,
		*do_aftertouch,
		*do_control,
		*do_patch,
		*do_channel_pressure,
		*do_pitch,
		*do_message
	};
	unsigned long int real_samples_to_mix = 0;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (handle == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	WM_Lock(&mdi->lock);
	if (sample_pos == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL seek position pointer)", 0);
		WM_Unlock(&mdi->lock);
		return -1;
	}

	if (*sample_pos == mdi->info.current_sample) {
		WM_Unlock(&mdi->lock);
		return 0;
	}

	if (*sample_pos > mdi->info.current_sample) {
		if ((mdi->sample_count == 0) && (mdi->index_count == mdi->index_size) && (mdi->last_note == 0)) {
			*sample_pos = mdi->info.current_sample;
			WM_Unlock(&mdi->lock);
			return 0;
		}
	} else {
		WM_ResetToStart(handle);
	}

	//reset all notes
	if (note_data != mdi->last_note) {
		do {
			(*note_data)->active = 0;
			*note_data = NULL;
			note_data++;
		} while (note_data != mdi->last_note);
		mdi->last_note = mdi->note;
	}

	while (*sample_pos != mdi->info.current_sample) {
		if (!mdi->sample_count) {
			if (mdi->index_count != mdi->index_size) {

				do {
					if (mdi->index_count == mdi->index_size) {
						break;
					}

					if (mdi->index_count != 0) {
						do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset);
					}
				} while (mdi->index[mdi->index_count++].delta == 0);

				mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta;
				mdi->sample_count = mdi->samples_to_mix >> 10;
				mdi->samples_to_mix %= 1024;
			} else {
				mdi->sample_count = WM_SampleRate;
			}
		}

		if (mdi->sample_count <= (*sample_pos - mdi->info.current_sample)) {
			real_samples_to_mix = mdi->sample_count;
			if (real_samples_to_mix == 0) {
				continue;
			}
		} else {
			real_samples_to_mix = (*sample_pos - mdi->info.current_sample);
		}

		mdi->info.current_sample += real_samples_to_mix;
		mdi->sample_count -= real_samples_to_mix;
		if ((mdi->index_count == mdi->index_size) && (mdi->last_note == 0)) {
			mdi->sample_count = 0;
			*sample_pos = mdi->info.current_sample;
			WM_Unlock(&mdi->lock);
			return 0;
		}
	}
	WM_Unlock(&mdi->lock);
	return 0;
}

int
WildMidi_SampledSeek ( midi * handle, unsigned long int *sample_pos) {
	struct _mdi *mdi = (struct _mdi *)handle;
	struct _note **note_data = mdi->note;
	unsigned long int real_samples_to_mix = 0;
	unsigned long int tmp_samples_to_mix = 0;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (handle == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	WM_Lock(&mdi->lock);
	if (sample_pos == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL seek position pointer)", 0);
		WM_Unlock(&mdi->lock);
		return -1;
	}

	if (*sample_pos == mdi->info.current_sample) {
		WM_Unlock(&mdi->lock);
		return 0;
	}

	if (*sample_pos > mdi->info.current_sample) {
		if ((mdi->sample_count == 0) && (mdi->index_count == mdi->index_size) && (mdi->last_note == 0)) {
			*sample_pos = mdi->info.current_sample;
			WM_Unlock(&mdi->lock);
			return 0;
		}
	} else {
		WM_ResetToStart(handle);
		if (note_data != mdi->last_note) {
			do {
				(*note_data)->active = 0;
				*note_data = NULL;
				note_data++;
			} while (note_data != mdi->last_note);
			mdi->last_note = mdi->note;
		}
	}

	while (*sample_pos != mdi->info.current_sample) {
		if (!mdi->sample_count) {
			if (mdi->index_count != mdi->index_size) {

				do {
					if (mdi->index_count == mdi->index_size) {
						break;
					}

					if (mdi->index_count != 0) {
						do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset);
					}
				} while (mdi->index[mdi->index_count++].delta == 0);

				mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta;
				mdi->sample_count = mdi->samples_to_mix >> 10;
				mdi->samples_to_mix %= 1024;
			} else {
				if (mdi->recalc_samples) {
					WM_RecalcSamples(mdi);
				}
				mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample;
				if (mdi->sample_count == 0) {
					WM_Unlock(&mdi->lock);
					return 0;
				}
			}
		}

		if (mdi->sample_count <= (*sample_pos - mdi->info.current_sample)) {
			real_samples_to_mix = mdi->sample_count;
			if (real_samples_to_mix == 0) {
				continue;
			}
		} else {
			real_samples_to_mix = (*sample_pos - mdi->info.current_sample);
		}

		// do mixing here
		tmp_samples_to_mix = real_samples_to_mix;
		do {

			if (mdi->last_note != mdi->note) {
				note_data = mdi->note;
				while (note_data != mdi->last_note) {


/*
 * ========================
 * sample position checking
 * ========================
 */
					(*note_data)->sample_pos += (*note_data)->sample_inc;
					if (__builtin_expect(((*note_data)->sample_pos > (*note_data)->sample->loop_end), 0)) {
						if ((*note_data)->modes & SAMPLE_LOOP) {
							(*note_data)->sample_pos = (*note_data)->sample->loop_start + (((*note_data)->sample_pos - (*note_data)->sample->loop_start) % (*note_data)->sample->loop_size);
						} else if (__builtin_expect(((*note_data)->sample_pos >= (*note_data)->sample->data_length), 0)) {
							if (__builtin_expect(((*note_data)->next == NULL), 1)) {
								goto KILL_NOTE;
							}
							goto RESTART_NOTE;
						}
					}
					if (__builtin_expect(((*note_data)->env_inc == 0), 0)) {
						note_data++;
						continue;
					}
					(*note_data)->env_level += (*note_data)->env_inc;
					if (__builtin_expect(((*note_data)->env_level > 4194304), 0)) {
						(*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env];
					}
					if (__builtin_expect((((*note_data)->env_inc < 0) &&
							((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env])) ||
							(((*note_data)->env_inc > 0) &&
							((*note_data)->env_level < (*note_data)->sample->env_target[(*note_data)->env])), 1)) {
						note_data++;
							continue;
					}
					(*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env];
					switch ((*note_data)->env) {
						case 0:
							if (!((*note_data)->modes & SAMPLE_ENVELOPE)) {
								(*note_data)->env_inc = 0;
								note_data++;
								continue;
							}
							break;
						case 2:
							if ((*note_data)->modes & SAMPLE_SUSTAIN) {
							(*note_data)->env_inc = 0;
							note_data++;
							continue;
						}
						break;
					case 5:
						if (__builtin_expect(((*note_data)->env_level == 0), 1)) {
							goto KILL_NOTE;
						}
						// sample release
						if ((*note_data)->modes & SAMPLE_LOOP)
							(*note_data)->modes ^= SAMPLE_LOOP;
						(*note_data)->env_inc = 0;
						note_data++;
						continue;
					case 6:
						if (__builtin_expect(((*note_data)->next != NULL), 1)) {
							RESTART_NOTE:
							(*note_data)->active = 0;
							*note_data = (*note_data)->next;
							(*note_data)->active = 1;
							note_data++;

						} else {
							KILL_NOTE:
							(*note_data)->active = 0;
							mdi->last_note--;
							if (note_data != mdi->last_note) {
								*note_data = *mdi->last_note;
							}
						}
						continue;
					}
					(*note_data)->env++;
					if ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env]) {
						(*note_data)->env_inc = -(*note_data)->sample->env_rate[(*note_data)->env];
					} else {
					(*note_data)->env_inc = (*note_data)->sample->env_rate[(*note_data)->env];
					}
					note_data++;
					continue;
				}
			} else {
				break;
			}
		} while (--tmp_samples_to_mix);
		mdi->info.current_sample += real_samples_to_mix;
		mdi->sample_count -= real_samples_to_mix;
		if (mdi->index_count == mdi->index_size) {
			if (mdi->last_note == 0) {
				mdi->sample_count = 0;
				*sample_pos = mdi->info.current_sample;
				WM_Unlock(&mdi->lock);
				return 0;
			}
		}
	}
	WM_Unlock(&mdi->lock);
	return 0;
}

int
WildMidi_GetOutput_Linear (midi * handle, char * buffer, unsigned long int size) {
	unsigned long int buffer_used = 0;
	struct _mdi *mdi = (struct _mdi *)handle;
	unsigned long int real_samples_to_mix = 0;
	unsigned long int data_pos;
	signed long int premix, left_mix, right_mix;
	signed long int vol_mul;
	struct _note **note_data = NULL;
	unsigned long int count;

	if (__builtin_expect((!WM_Initialized),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (__builtin_expect((handle == NULL),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	if (__builtin_expect((buffer == NULL),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0);
		return -1;
	}

	if (__builtin_expect((size == 0),0)) {
		return 0;
	}

	if (__builtin_expect((size % 4),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0);
		return -1;
	}

	WM_Lock(&mdi->lock);
	if (__builtin_expect(((mdi->index_count == mdi->index_size) && (mdi->last_note == 0)), 0)) {
		WM_Unlock(&mdi->lock);
		return 0;
	}

	buffer_used = 0;
	memset(buffer, 0, size);

	do {
		if (__builtin_expect((!mdi->sample_count),0)) {
			if (__builtin_expect((mdi->index_count != mdi->index_size),1)) {
				do {
					if (__builtin_expect((mdi->index_count == mdi->index_size), 0)) {
						break;
					}

					if (__builtin_expect((mdi->index_count != 0), 1)) {
						do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset);
					}
				} while (mdi->index[mdi->index_count++].delta == 0);

				mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta;
				mdi->sample_count = mdi->samples_to_mix >> 10;
				mdi->samples_to_mix %= 1024;
			} else {
				if (mdi->recalc_samples) {
					WM_RecalcSamples(mdi);
				}
				mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample;
				if (mdi->sample_count == 0) {
					WM_Unlock(&mdi->lock);
					return buffer_used;
				}
			}
		}
		if (__builtin_expect((mdi->sample_count > (size >> 2)),1)) {
			real_samples_to_mix = size >> 2;
		} else {
			real_samples_to_mix = mdi->sample_count;
			if (real_samples_to_mix == 0) {
				continue;
			}
		}

		// do mixing here
		count = real_samples_to_mix;
		do {
			note_data = mdi->note;
			left_mix = right_mix = 0;
			if (__builtin_expect((mdi->last_note != mdi->note),1)) {
				while (note_data != mdi->last_note) {
/*
 * ===================
 * resample the sample
 * ===================
 */
					data_pos = (*note_data)->sample_pos >> FPBITS;
					vol_mul = (((*note_data)->vol_lvl * ((*note_data)->env_level >> 12)) >> FPBITS);

					premix = ((*note_data)->sample->data[data_pos] +
						(((*note_data)->sample->data[data_pos + 1]  - (*note_data)->sample->data[data_pos]) *
						(signed long int)((*note_data)->sample_pos & FPMASK) >> FPBITS)) * vol_mul / 1024;

					left_mix += premix * mdi->channel[(*note_data)->noteid >> 8].left_adjust;
					right_mix += premix * mdi->channel[(*note_data)->noteid >> 8].right_adjust;

/*
 * ========================
 * sample position checking
 * ========================
 */
					(*note_data)->sample_pos += (*note_data)->sample_inc;
					if (__builtin_expect(((*note_data)->sample_pos > (*note_data)->sample->loop_end), 0)) {
						if ((*note_data)->modes & SAMPLE_LOOP) {
							(*note_data)->sample_pos = (*note_data)->sample->loop_start + (((*note_data)->sample_pos - (*note_data)->sample->loop_start) % (*note_data)->sample->loop_size);
						} else if (__builtin_expect(((*note_data)->sample_pos >= (*note_data)->sample->data_length), 0)) {
							if (__builtin_expect(((*note_data)->next == NULL), 1)) {
								goto KILL_NOTE;

							}
							goto RESTART_NOTE;
						}
					}

					if (__builtin_expect(((*note_data)->env_inc == 0), 0)) {
						note_data++;
						continue;
					}

					(*note_data)->env_level += (*note_data)->env_inc;
					if (__builtin_expect(((*note_data)->env_level > 4194304), 0)) {
						(*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env];
					}
					if (__builtin_expect((((*note_data)->env_inc < 0) &&
							((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env])) ||
							(((*note_data)->env_inc > 0) &&
							((*note_data)->env_level < (*note_data)->sample->env_target[(*note_data)->env])), 1)) {
						note_data++;
						continue;
					}

					(*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env];
					switch ((*note_data)->env) {
						case 0:
							if (!((*note_data)->modes & SAMPLE_ENVELOPE)) {
								(*note_data)->env_inc = 0;
								note_data++;
								continue;
							}
							break;
						case 2:
							if ((*note_data)->modes & SAMPLE_SUSTAIN) {
								(*note_data)->env_inc = 0;
								note_data++;
								continue;
							}
							break;
						case 5:
							if (__builtin_expect(((*note_data)->env_level == 0), 1)) {
								goto KILL_NOTE;
							}
							// sample release
							if ((*note_data)->modes & SAMPLE_LOOP)
								(*note_data)->modes ^= SAMPLE_LOOP;
							(*note_data)->env_inc = 0;
							note_data++;
							continue;
						case 6:
							if (__builtin_expect(((*note_data)->next != NULL), 1)) {
								RESTART_NOTE:
								(*note_data)->active = 0;
								*note_data = (*note_data)->next;
								(*note_data)->active = 1;
								note_data++;

							} else {
								KILL_NOTE:
								(*note_data)->active = 0;
								mdi->last_note--;
								if (note_data != mdi->last_note) {
									*note_data = *mdi->last_note;
								}
							}
							continue;
					}
					(*note_data)->env++;
					if ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env]) {
						(*note_data)->env_inc = -(*note_data)->sample->env_rate[(*note_data)->env];
					} else {
						(*note_data)->env_inc = (*note_data)->sample->env_rate[(*note_data)->env];
					}
					note_data++;
					continue;
				}
/*
 * =========================
 * mix the channels together
 * =========================
 */

				left_mix /= 1024;
				right_mix /= 1024;
			}

#ifdef EXPERIMENT_626
/*
 * ==========================
 * Experimental Reverb Engine
 * ==========================
 */

			if (mdi->info.mixer_options & WM_MO_REVERB) {
				signed long int filteral = mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]];
				signed long int filterar = mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]];
				signed long int filterbl = mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]];
				signed long int filterbr = mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]];
				signed long int filtercl = mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]];
				signed long int filtercr = mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]];
				signed long int filterdl = mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]];
				signed long int filterdr = mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]];
				signed long int tfal = (a[0][0] * filteral + a[0][1] * mdi->filter.lowpass[0][0].in[0] + a[0][0] * mdi->filter.lowpass[0][0].in[1] - b[0][0] * mdi->filter.lowpass[0][0].out[0] - b[0][1] * mdi->filter.lowpass[0][0].out[1]) / 1024;
				signed long int tfar = (a[0][0] * filterar + a[0][1] * mdi->filter.lowpass[0][1].in[0] + a[0][0] * mdi->filter.lowpass[0][1].in[1] - b[0][0] * mdi->filter.lowpass[0][1].out[0] - b[0][1] * mdi->filter.lowpass[0][1].out[1]) / 1024;
				signed long int tfbl = (a[1][0] * filterbl + a[1][1] * mdi->filter.lowpass[1][0].in[0] + a[1][0] * mdi->filter.lowpass[1][0].in[1] - b[1][0] * mdi->filter.lowpass[1][0].out[0] - b[1][1] * mdi->filter.lowpass[1][0].out[1]) / 1024;
				signed long int tfbr = (a[1][0] * filterbr + a[1][1] * mdi->filter.lowpass[1][1].in[0] + a[1][0] * mdi->filter.lowpass[1][1].in[1] - b[1][0] * mdi->filter.lowpass[1][1].out[0] - b[1][1] * mdi->filter.lowpass[1][1].out[1]) / 1024;
				signed long int tfcl = (a[2][0] * filtercl + a[2][1] * mdi->filter.lowpass[2][0].in[0] + a[2][0] * mdi->filter.lowpass[2][0].in[1] - b[2][0] * mdi->filter.lowpass[2][0].out[0] - b[2][1] * mdi->filter.lowpass[2][0].out[1]) / 1024;
				signed long int tfcr = (a[2][0] * filtercr + a[2][1] * mdi->filter.lowpass[2][1].in[0] + a[2][0] * mdi->filter.lowpass[2][1].in[1] - b[2][0] * mdi->filter.lowpass[2][1].out[0] - b[2][1] * mdi->filter.lowpass[2][1].out[1]) / 1024;
				signed long int tfdl = (a[3][0] * filterdl + a[3][1] * mdi->filter.lowpass[3][0].in[0] + a[3][0] * mdi->filter.lowpass[3][0].in[1] - b[3][0] * mdi->filter.lowpass[3][0].out[0] - b[3][1] * mdi->filter.lowpass[3][0].out[1]) / 1024;
				signed long int tfdr = (a[3][0] * filterdr + a[3][1] * mdi->filter.lowpass[3][1].in[0] + a[3][0] * mdi->filter.lowpass[3][1].in[1] - b[3][0] * mdi->filter.lowpass[3][1].out[0] - b[3][1] * mdi->filter.lowpass[3][1].out[1]) / 1024;
				signed long int tfl, tflo;
				signed long int tfr, tfro;

				mdi->filter.lowpass[0][0].in[1] = mdi->filter.lowpass[0][0].in[0];
				mdi->filter.lowpass[0][0].in[0] = filteral;
				mdi->filter.lowpass[0][1].in[1] = mdi->filter.lowpass[0][1].in[0];
				mdi->filter.lowpass[0][1].in[0] = filterar;
				mdi->filter.lowpass[1][0].in[1] = mdi->filter.lowpass[1][0].in[0];
				mdi->filter.lowpass[1][0].in[0] = filterbl;
				mdi->filter.lowpass[1][1].in[1] = mdi->filter.lowpass[1][1].in[0];
				mdi->filter.lowpass[1][1].in[0] = filterbr;
				mdi->filter.lowpass[2][0].in[1] = mdi->filter.lowpass[2][0].in[0];
				mdi->filter.lowpass[2][0].in[0] = filtercl;
				mdi->filter.lowpass[2][1].in[1] = mdi->filter.lowpass[2][1].in[0];
				mdi->filter.lowpass[2][1].in[0] = filtercr;
				mdi->filter.lowpass[3][0].in[1] = mdi->filter.lowpass[3][0].in[0];
				mdi->filter.lowpass[3][0].in[0] = filterdl;
				mdi->filter.lowpass[3][1].in[1] = mdi->filter.lowpass[3][1].in[0];
				mdi->filter.lowpass[3][1].in[0] = filterdr;

				mdi->filter.lowpass[0][0].out[1] = mdi->filter.lowpass[0][0].out[0];
				mdi->filter.lowpass[0][0].out[0] = tfal;
				mdi->filter.lowpass[0][1].out[1] = mdi->filter.lowpass[0][1].out[0];
				mdi->filter.lowpass[0][1].out[0] = tfar;
				mdi->filter.lowpass[1][0].out[1] = mdi->filter.lowpass[1][0].out[0];
				mdi->filter.lowpass[1][0].out[0] = tfbl;
				mdi->filter.lowpass[1][1].out[1] = mdi->filter.lowpass[1][1].out[0];
				mdi->filter.lowpass[1][1].out[0] = tfbr;
				mdi->filter.lowpass[2][0].out[1] = mdi->filter.lowpass[2][0].out[0];
				mdi->filter.lowpass[2][0].out[0] = tfcl;
				mdi->filter.lowpass[2][1].out[1] = mdi->filter.lowpass[2][1].out[0];
				mdi->filter.lowpass[2][1].out[0] = tfcr;
				mdi->filter.lowpass[3][0].out[1] = mdi->filter.lowpass[3][0].out[0];
				mdi->filter.lowpass[3][0].out[0] = tfdl;
				mdi->filter.lowpass[3][1].out[1] = mdi->filter.lowpass[3][1].out[0];
				mdi->filter.lowpass[3][1].out[0] = tfdr;

				mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]] = (tfbr * 405 + tfcr * 368) / 1024 + (left_mix * gain_in[0] / 1024);
				mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]] = (tfbl * 402 + tfcl * 370) / 1024 + (right_mix * gain_in[0] / 1024);
				mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]] = (tfar * -545 + tfdr * -364) / 1024 + (left_mix * gain_in[1] / 1024);
				mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]] = (tfal * -550 + tfdl * -362) / 1024 + (right_mix * gain_in[1] / 1024);
				mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]] = (tfar * 545 + tfdr * -364) / 1024 + (left_mix * gain_in[2] / 1024);
				mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]] = (tfal * 550 + tfdl * 362) / 1024 + (right_mix * gain_in[2] / 1024);
				mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]] = (tfbr * 405 + tfcr * -368) / 1024 + (left_mix * gain_in[3] / 1024);
				mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]] = (tfbl * 402 + tfcl * -370) / 1024 + (right_mix * gain_in[3] / 1024);

				tfl = ((tfal * gain_out[0] / 1024) + (tfbl * gain_out[1] / 1024) + (tfcl * gain_out[2] / 1024) + (tfdl * gain_out[3] / 1024));
				tfr = ((tfar * gain_out[0] / 1024) + (tfbr * gain_out[1] / 1024) + (tfcr * gain_out[2] / 1024) + (tfdr * gain_out[3] / 1024));

				tflo = (a[4][0] * tfl + a[4][1] * mdi->filter.in[0][0] + a[4][0] * mdi->filter.in[1][0] - b[4][0] * mdi->filter.out[0][0] - b[4][1] * mdi->filter.out[1][0]) / 1024;
				tfro = (a[4][0] * tfr + a[4][1] * mdi->filter.in[0][1] + a[4][0] * mdi->filter.in[1][1] - b[4][0] * mdi->filter.out[0][1] - b[4][1] * mdi->filter.out[1][1]) / 1024;

				mdi->filter.in[1][0] =  mdi->filter.in[0][0];
				mdi->filter.in[0][0] = tfl;
				mdi->filter.in[1][1] =  mdi->filter.in[0][1];
				mdi->filter.in[0][1] = tfr;
				mdi->filter.out[1][0] =  mdi->filter.out[0][0];
				mdi->filter.out[0][0] = tflo;
				mdi->filter.out[1][1] =  mdi->filter.out[0][1];
				mdi->filter.out[0][1] = tfro;

				left_mix += tflo;
				right_mix += tfro;

				mdi->filter.delay_pos[0][0]++;
				mdi->filter.delay_pos[0][1]++;
				mdi->filter.delay_pos[1][0]++;
				mdi->filter.delay_pos[1][1]++;
				mdi->filter.delay_pos[2][0]++;
				mdi->filter.delay_pos[2][1]++;
				mdi->filter.delay_pos[3][0]++;
				mdi->filter.delay_pos[3][1]++;

				if (mdi->filter.delay_pos[0][0] == delay_size[0][0]) mdi->filter.delay_pos[0][0] = 0;
				if (mdi->filter.delay_pos[0][1] == delay_size[0][1]) mdi->filter.delay_pos[0][1] = 0;
				if (mdi->filter.delay_pos[1][0] == delay_size[1][0]) mdi->filter.delay_pos[1][0] = 0;
				if (mdi->filter.delay_pos[1][1] == delay_size[1][1]) mdi->filter.delay_pos[1][1] = 0;
				if (mdi->filter.delay_pos[2][0] == delay_size[2][0]) mdi->filter.delay_pos[2][0] = 0;
				if (mdi->filter.delay_pos[2][1] == delay_size[2][1]) mdi->filter.delay_pos[2][1] = 0;
				if (mdi->filter.delay_pos[3][0] == delay_size[3][0]) mdi->filter.delay_pos[3][0] = 0;
				if (mdi->filter.delay_pos[3][1] == delay_size[3][1]) mdi->filter.delay_pos[3][1] = 0;

			}
#endif
			if (left_mix > 32767) {
				left_mix = 32767;
			} else if (left_mix < -32768) {
				left_mix = -32768;
			}

			if (right_mix > 32767) {
				right_mix = 32767;
			} else if (right_mix < -32768) {
				right_mix = -32768;
			}


/*
 * ===================
 * Write to the buffer
 * ===================
 */
			(*buffer++) = left_mix & 0xff;
			(*buffer++) = (left_mix >> 8) & 0xff;
			(*buffer++) = right_mix & 0xff;
			(*buffer++) = (right_mix >> 8) & 0xff;
		} while (--count);

		buffer_used += real_samples_to_mix * 4;
		size -= (real_samples_to_mix << 2);
		mdi->info.current_sample += real_samples_to_mix;
		mdi->sample_count -= real_samples_to_mix;
		if (mdi->index_count == mdi->index_size) {
			if (mdi->last_note == 0) {
				mdi->sample_count = 0;
				WM_Unlock(&mdi->lock);
				return buffer_used;
			}
		}
	} while (size);

	if ((mdi->index_count == mdi->index_size) && (mdi->recalc_samples)) {
		WM_RecalcSamples(mdi);
		mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample;
	}
	WM_Unlock(&mdi->lock);
	return buffer_used;
}

int
WildMidi_GetOutput_Gauss (midi * handle, char * buffer, unsigned long int size) {
	unsigned long int buffer_used = 0;
	struct _mdi *mdi = (struct _mdi *)handle;
	unsigned long int real_samples_to_mix = 0;
	unsigned long int data_pos;
	signed long int premix, left_mix, right_mix;
	signed long int vol_mul;
	struct _note **note_data = NULL;
	unsigned long int count;
	signed short int *sptr;
	double y, xd;
	float *gptr, *gend;
	int left, right, temp_n;
	int ii, jj;


	if (__builtin_expect((!WM_Initialized),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (__builtin_expect((handle == NULL),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	if (__builtin_expect((buffer == NULL),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0);
		return -1;
	}

	if (__builtin_expect((size == 0),0)) {
		return 0;
	}

	if (__builtin_expect((size % 4),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0);
		return -1;
	}

	WM_Lock(&mdi->lock);
	if (__builtin_expect(((mdi->index_count == mdi->index_size) && (mdi->last_note == 0)), 0)) {
		WM_Unlock(&mdi->lock);
		return 0;
	}

	buffer_used = 0;
	memset(buffer, 0, size);

	do {
		if (__builtin_expect((!mdi->sample_count),0)) {
			if (__builtin_expect((mdi->index_count != mdi->index_size),1)) {
				do {
					if (__builtin_expect((mdi->index_count == mdi->index_size), 0)) {
						break;
					}

					if (__builtin_expect((mdi->index_count != 0), 1)) {
						do_event[((mdi->index[mdi->index_count].event & 0xF0) >> 4) - 8]((mdi->index[mdi->index_count].event & 0x0F), mdi, mdi->index[mdi->index_count].offset);
					}
				} while (mdi->index[mdi->index_count++].delta == 0);

				mdi->samples_to_mix += mdi->index[mdi->index_count-1].delta * mdi->samples_per_delta;
				mdi->sample_count = mdi->samples_to_mix >> 10;
				mdi->samples_to_mix %= 1024;
			} else {
				if (mdi->recalc_samples) {
					WM_RecalcSamples(mdi);
				}
				mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample;
				if (mdi->sample_count == 0) {
					WM_Unlock(&mdi->lock);
					return buffer_used;
				}
			}
		}
		if (__builtin_expect((mdi->sample_count > (size >> 2)),1)) {
			real_samples_to_mix = size >> 2;
		} else {
			real_samples_to_mix = mdi->sample_count;
			if (real_samples_to_mix == 0) {
				continue;
			}
		}

		// do mixing here
		count = real_samples_to_mix;
		do {
			note_data = mdi->note;
			left_mix = right_mix = 0;
			if (__builtin_expect((mdi->last_note != mdi->note),1)) {
				while (note_data != mdi->last_note) {
/*
 * ===================
 * resample the sample
 * ===================
 */
					data_pos = (*note_data)->sample_pos >> FPBITS;
					vol_mul = (((*note_data)->vol_lvl * ((*note_data)->env_level >> 12)) >> FPBITS);

					/* check to see if we're near one of the ends */
					left = data_pos;
					right = ((*note_data)->sample->data_length>>FPBITS)- left -1;
					temp_n = (right<<1)-1;
					if (temp_n <= 0)
						temp_n = 1;
					if (temp_n > (left<<1)+1)
						temp_n = (left<<1)+1;

					/* use Newton if we can't fill the window */
					if (temp_n < gauss_n) {
						xd = (*note_data)->sample_pos & FPMASK;
						xd /= (1L<<FPBITS);
						xd += temp_n>>1;
						y = 0;
						sptr = (*note_data)->sample->data + ((*note_data)->sample_pos>>FPBITS) - (temp_n>>1);
						for (ii = temp_n; ii;) {
							for (jj = 0; jj <= ii; jj++)
								y += sptr[jj] * newt_coeffs[ii][jj];
							y *= xd - --ii;
						}
						y += *sptr;
					} else {			/* otherwise, use Gauss as usual */
						y = 0;
						gptr = gauss_table[(*note_data)->sample_pos & FPMASK];
						gend = gptr + gauss_n;
						sptr = (*note_data)->sample->data + ((*note_data)->sample_pos >> FPBITS) - (gauss_n>>1);
						do {
							y += *(sptr++) * *(gptr++);
						} while (gptr <= gend);
					}

					premix = y * vol_mul / 1024;

					left_mix += premix * mdi->channel[(*note_data)->noteid >> 8].left_adjust;
					right_mix += premix * mdi->channel[(*note_data)->noteid >> 8].right_adjust;

/*
 * ========================
 * sample position checking
 * ========================
 */
					(*note_data)->sample_pos += (*note_data)->sample_inc;
					if (__builtin_expect(((*note_data)->sample_pos > (*note_data)->sample->loop_end), 0)) {
						if ((*note_data)->modes & SAMPLE_LOOP) {
							(*note_data)->sample_pos = (*note_data)->sample->loop_start + (((*note_data)->sample_pos - (*note_data)->sample->loop_start) % (*note_data)->sample->loop_size);
						} else if (__builtin_expect(((*note_data)->sample_pos >= (*note_data)->sample->data_length), 0)) {
							if (__builtin_expect(((*note_data)->next == NULL), 1)) {
								goto KILL_NOTE;

							}
							goto RESTART_NOTE;
						}
					}

					if (__builtin_expect(((*note_data)->env_inc == 0), 0)) {
						note_data++;
						continue;
					}

					(*note_data)->env_level += (*note_data)->env_inc;
					if (__builtin_expect(((*note_data)->env_level > 4194304), 0)) {
						(*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env];
					}
					if (__builtin_expect((((*note_data)->env_inc < 0) &&
							((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env])) ||
							(((*note_data)->env_inc > 0) &&
							((*note_data)->env_level < (*note_data)->sample->env_target[(*note_data)->env])), 1)) {
						note_data++;
						continue;
					}

					(*note_data)->env_level = (*note_data)->sample->env_target[(*note_data)->env];
					switch ((*note_data)->env) {
						case 0:
							if (!((*note_data)->modes & SAMPLE_ENVELOPE)) {
								(*note_data)->env_inc = 0;
								note_data++;
								continue;
							}
							break;
						case 2:
							if ((*note_data)->modes & SAMPLE_SUSTAIN) {
								(*note_data)->env_inc = 0;
								note_data++;
								continue;
							}
							break;
						case 5:
							if (__builtin_expect(((*note_data)->env_level == 0), 1)) {
								goto KILL_NOTE;
							}
							// sample release
							if ((*note_data)->modes & SAMPLE_LOOP)
								(*note_data)->modes ^= SAMPLE_LOOP;
							(*note_data)->env_inc = 0;
							note_data++;
							continue;
						case 6:
							if (__builtin_expect(((*note_data)->next != NULL), 1)) {
								RESTART_NOTE:
								(*note_data)->active = 0;
								*note_data = (*note_data)->next;
								(*note_data)->active = 1;
								note_data++;

							} else {
								KILL_NOTE:
								(*note_data)->active = 0;
								mdi->last_note--;
								if (note_data != mdi->last_note) {
									*note_data = *mdi->last_note;
								}
							}
							continue;
					}
					(*note_data)->env++;
					if ((*note_data)->env_level > (*note_data)->sample->env_target[(*note_data)->env]) {
						(*note_data)->env_inc = -(*note_data)->sample->env_rate[(*note_data)->env];
					} else {
						(*note_data)->env_inc = (*note_data)->sample->env_rate[(*note_data)->env];
					}
					note_data++;
					continue;
				}
/*
 * =========================
 * mix the channels together
 * =========================
 */

				left_mix /= 1024;
				right_mix /= 1024;
			}

#ifdef EXPERIMENT_626
/*
 * ==========================
 * Experimental Reverb Engine
 * ==========================
 */

			if (mdi->info.mixer_options & WM_MO_REVERB) {
				signed long int filteral = mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]];
				signed long int filterar = mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]];
				signed long int filterbl = mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]];
				signed long int filterbr = mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]];
				signed long int filtercl = mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]];
				signed long int filtercr = mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]];
				signed long int filterdl = mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]];
				signed long int filterdr = mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]];
				signed long int tfal = (a[0][0] * filteral + a[0][1] * mdi->filter.lowpass[0][0].in[0] + a[0][0] * mdi->filter.lowpass[0][0].in[1] - b[0][0] * mdi->filter.lowpass[0][0].out[0] - b[0][1] * mdi->filter.lowpass[0][0].out[1]) / 1024;
				signed long int tfar = (a[0][0] * filterar + a[0][1] * mdi->filter.lowpass[0][1].in[0] + a[0][0] * mdi->filter.lowpass[0][1].in[1] - b[0][0] * mdi->filter.lowpass[0][1].out[0] - b[0][1] * mdi->filter.lowpass[0][1].out[1]) / 1024;
				signed long int tfbl = (a[1][0] * filterbl + a[1][1] * mdi->filter.lowpass[1][0].in[0] + a[1][0] * mdi->filter.lowpass[1][0].in[1] - b[1][0] * mdi->filter.lowpass[1][0].out[0] - b[1][1] * mdi->filter.lowpass[1][0].out[1]) / 1024;
				signed long int tfbr = (a[1][0] * filterbr + a[1][1] * mdi->filter.lowpass[1][1].in[0] + a[1][0] * mdi->filter.lowpass[1][1].in[1] - b[1][0] * mdi->filter.lowpass[1][1].out[0] - b[1][1] * mdi->filter.lowpass[1][1].out[1]) / 1024;
				signed long int tfcl = (a[2][0] * filtercl + a[2][1] * mdi->filter.lowpass[2][0].in[0] + a[2][0] * mdi->filter.lowpass[2][0].in[1] - b[2][0] * mdi->filter.lowpass[2][0].out[0] - b[2][1] * mdi->filter.lowpass[2][0].out[1]) / 1024;
				signed long int tfcr = (a[2][0] * filtercr + a[2][1] * mdi->filter.lowpass[2][1].in[0] + a[2][0] * mdi->filter.lowpass[2][1].in[1] - b[2][0] * mdi->filter.lowpass[2][1].out[0] - b[2][1] * mdi->filter.lowpass[2][1].out[1]) / 1024;
				signed long int tfdl = (a[3][0] * filterdl + a[3][1] * mdi->filter.lowpass[3][0].in[0] + a[3][0] * mdi->filter.lowpass[3][0].in[1] - b[3][0] * mdi->filter.lowpass[3][0].out[0] - b[3][1] * mdi->filter.lowpass[3][0].out[1]) / 1024;
				signed long int tfdr = (a[3][0] * filterdr + a[3][1] * mdi->filter.lowpass[3][1].in[0] + a[3][0] * mdi->filter.lowpass[3][1].in[1] - b[3][0] * mdi->filter.lowpass[3][1].out[0] - b[3][1] * mdi->filter.lowpass[3][1].out[1]) / 1024;
				signed long int tfl, tflo;
				signed long int tfr, tfro;

				mdi->filter.lowpass[0][0].in[1] = mdi->filter.lowpass[0][0].in[0];
				mdi->filter.lowpass[0][0].in[0] = filteral;
				mdi->filter.lowpass[0][1].in[1] = mdi->filter.lowpass[0][1].in[0];
				mdi->filter.lowpass[0][1].in[0] = filterar;
				mdi->filter.lowpass[1][0].in[1] = mdi->filter.lowpass[1][0].in[0];
				mdi->filter.lowpass[1][0].in[0] = filterbl;
				mdi->filter.lowpass[1][1].in[1] = mdi->filter.lowpass[1][1].in[0];
				mdi->filter.lowpass[1][1].in[0] = filterbr;
				mdi->filter.lowpass[2][0].in[1] = mdi->filter.lowpass[2][0].in[0];
				mdi->filter.lowpass[2][0].in[0] = filtercl;
				mdi->filter.lowpass[2][1].in[1] = mdi->filter.lowpass[2][1].in[0];
				mdi->filter.lowpass[2][1].in[0] = filtercr;
				mdi->filter.lowpass[3][0].in[1] = mdi->filter.lowpass[3][0].in[0];
				mdi->filter.lowpass[3][0].in[0] = filterdl;
				mdi->filter.lowpass[3][1].in[1] = mdi->filter.lowpass[3][1].in[0];
				mdi->filter.lowpass[3][1].in[0] = filterdr;

				mdi->filter.lowpass[0][0].out[1] = mdi->filter.lowpass[0][0].out[0];
				mdi->filter.lowpass[0][0].out[0] = tfal;
				mdi->filter.lowpass[0][1].out[1] = mdi->filter.lowpass[0][1].out[0];
				mdi->filter.lowpass[0][1].out[0] = tfar;
				mdi->filter.lowpass[1][0].out[1] = mdi->filter.lowpass[1][0].out[0];
				mdi->filter.lowpass[1][0].out[0] = tfbl;
				mdi->filter.lowpass[1][1].out[1] = mdi->filter.lowpass[1][1].out[0];
				mdi->filter.lowpass[1][1].out[0] = tfbr;
				mdi->filter.lowpass[2][0].out[1] = mdi->filter.lowpass[2][0].out[0];
				mdi->filter.lowpass[2][0].out[0] = tfcl;
				mdi->filter.lowpass[2][1].out[1] = mdi->filter.lowpass[2][1].out[0];
				mdi->filter.lowpass[2][1].out[0] = tfcr;
				mdi->filter.lowpass[3][0].out[1] = mdi->filter.lowpass[3][0].out[0];
				mdi->filter.lowpass[3][0].out[0] = tfdl;
				mdi->filter.lowpass[3][1].out[1] = mdi->filter.lowpass[3][1].out[0];
				mdi->filter.lowpass[3][1].out[0] = tfdr;

				mdi->filter.delay[0][0][mdi->filter.delay_pos[0][0]] = (tfbr * 405 + tfcr * 368) / 1024 + (left_mix * gain_in[0] / 1024);
				mdi->filter.delay[0][1][mdi->filter.delay_pos[0][1]] = (tfbl * 402 + tfcl * 370) / 1024 + (right_mix * gain_in[0] / 1024);
				mdi->filter.delay[1][0][mdi->filter.delay_pos[1][0]] = (tfar * -545 + tfdr * -364) / 1024 + (left_mix * gain_in[1] / 1024);
				mdi->filter.delay[1][1][mdi->filter.delay_pos[1][1]] = (tfal * -550 + tfdl * -362) / 1024 + (right_mix * gain_in[1] / 1024);
				mdi->filter.delay[2][0][mdi->filter.delay_pos[2][0]] = (tfar * 545 + tfdr * -364) / 1024 + (left_mix * gain_in[2] / 1024);
				mdi->filter.delay[2][1][mdi->filter.delay_pos[2][1]] = (tfal * 550 + tfdl * 362) / 1024 + (right_mix * gain_in[2] / 1024);
				mdi->filter.delay[3][0][mdi->filter.delay_pos[3][0]] = (tfbr * 405 + tfcr * -368) / 1024 + (left_mix * gain_in[3] / 1024);
				mdi->filter.delay[3][1][mdi->filter.delay_pos[3][1]] = (tfbl * 402 + tfcl * -370) / 1024 + (right_mix * gain_in[3] / 1024);

				tfl = ((tfal * gain_out[0] / 1024) + (tfbl * gain_out[1] / 1024) + (tfcl * gain_out[2] / 1024) + (tfdl * gain_out[3] / 1024));
				tfr = ((tfar * gain_out[0] / 1024) + (tfbr * gain_out[1] / 1024) + (tfcr * gain_out[2] / 1024) + (tfdr * gain_out[3] / 1024));

				tflo = (a[4][0] * tfl + a[4][1] * mdi->filter.in[0][0] + a[4][0] * mdi->filter.in[1][0] - b[4][0] * mdi->filter.out[0][0] - b[4][1] * mdi->filter.out[1][0]) / 1024;
				tfro = (a[4][0] * tfr + a[4][1] * mdi->filter.in[0][1] + a[4][0] * mdi->filter.in[1][1] - b[4][0] * mdi->filter.out[0][1] - b[4][1] * mdi->filter.out[1][1]) / 1024;

				mdi->filter.in[1][0] =  mdi->filter.in[0][0];
				mdi->filter.in[0][0] = tfl;
				mdi->filter.in[1][1] =  mdi->filter.in[0][1];
				mdi->filter.in[0][1] = tfr;
				mdi->filter.out[1][0] =  mdi->filter.out[0][0];
				mdi->filter.out[0][0] = tflo;
				mdi->filter.out[1][1] =  mdi->filter.out[0][1];
				mdi->filter.out[0][1] = tfro;

				left_mix += tflo;
				right_mix += tfro;

				mdi->filter.delay_pos[0][0]++;
				mdi->filter.delay_pos[0][1]++;
				mdi->filter.delay_pos[1][0]++;
				mdi->filter.delay_pos[1][1]++;
				mdi->filter.delay_pos[2][0]++;
				mdi->filter.delay_pos[2][1]++;
				mdi->filter.delay_pos[3][0]++;
				mdi->filter.delay_pos[3][1]++;

				if (mdi->filter.delay_pos[0][0] == delay_size[0][0]) mdi->filter.delay_pos[0][0] = 0;
				if (mdi->filter.delay_pos[0][1] == delay_size[0][1]) mdi->filter.delay_pos[0][1] = 0;
				if (mdi->filter.delay_pos[1][0] == delay_size[1][0]) mdi->filter.delay_pos[1][0] = 0;
				if (mdi->filter.delay_pos[1][1] == delay_size[1][1]) mdi->filter.delay_pos[1][1] = 0;
				if (mdi->filter.delay_pos[2][0] == delay_size[2][0]) mdi->filter.delay_pos[2][0] = 0;
				if (mdi->filter.delay_pos[2][1] == delay_size[2][1]) mdi->filter.delay_pos[2][1] = 0;
				if (mdi->filter.delay_pos[3][0] == delay_size[3][0]) mdi->filter.delay_pos[3][0] = 0;
				if (mdi->filter.delay_pos[3][1] == delay_size[3][1]) mdi->filter.delay_pos[3][1] = 0;

			}
#endif
			if (left_mix > 32767) {
				left_mix = 32767;
			} else if (left_mix < -32768) {
				left_mix = -32768;
			}

			if (right_mix > 32767) {
				right_mix = 32767;
			} else if (right_mix < -32768) {
				right_mix = -32768;
			}


/*
 * ===================
 * Write to the buffer
 * ===================
 */
			(*buffer++) = left_mix & 0xff;
			(*buffer++) = (left_mix >> 8) & 0xff;
			(*buffer++) = right_mix & 0xff;
			(*buffer++) = (right_mix >> 8) & 0xff;
		} while (--count);

		buffer_used += real_samples_to_mix * 4;
		size -= (real_samples_to_mix << 2);
		mdi->info.current_sample += real_samples_to_mix;
		mdi->sample_count -= real_samples_to_mix;
		if (mdi->index_count == mdi->index_size) {
			if (mdi->last_note == 0) {
				mdi->sample_count = 0;
				WM_Unlock(&mdi->lock);
				return buffer_used;
			}
		}
	} while (size);

	if ((mdi->index_count == mdi->index_size) && (mdi->recalc_samples)) {
		WM_RecalcSamples(mdi);
		mdi->sample_count = mdi->info.approx_total_samples - mdi->info.current_sample;
	}
	WM_Unlock(&mdi->lock);
	return buffer_used;
}

int
WildMidi_GetOutput (midi * handle, char * buffer, unsigned long int size) {
	struct _mdi *mdi = (struct _mdi *)handle;

	if (__builtin_expect((!WM_Initialized),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (__builtin_expect((handle == NULL),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	if (__builtin_expect((buffer == NULL),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0);
		return -1;
	}

	if (__builtin_expect((size == 0),0)) {
		return 0;
	}

	if (__builtin_expect((size % 4),0)) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0);
		return -1;
	}
	if (mdi->info.mixer_options & WM_MO_EXPENSIVE_INTERPOLATION) {
		return WildMidi_GetOutput_Gauss (handle, buffer,size);
	} else {
		return WildMidi_GetOutput_Linear (handle, buffer, size);
	}
}

int
WildMidi_SetOption (midi * handle, unsigned short int options, unsigned short int setting) {
	struct _mdi *mdi = (struct _mdi *)handle;
	struct _note **note_data = mdi->note;
	int i;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (handle == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return -1;
	}
	WM_Lock(&mdi->lock);
	if ((!(options & 0x0007)) || (options & 0xFFF8)){
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0);
		WM_Unlock(&mdi->lock);
		return -1;
	}
	if (setting & 0xFFF8) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid setting)", 0);
		WM_Unlock(&mdi->lock);
		return -1;
	}

	mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options)) | (options & setting));

	if (options & WM_MO_LINEAR_VOLUME) {
		if (mdi->info.mixer_options & WM_MO_LINEAR_VOLUME) {
			mdi->amp = 281;
		} else {
			mdi->amp = 281 * mdi->lin_max_vol / mdi->log_max_vol;
		}
		for (i = 0; i < 16; i++) {
			do_pan_adjust(mdi, i);
		}
		if (note_data != mdi->last_note) {
			do {
				(*note_data)->vol_lvl = get_volume(mdi, ((*note_data)->noteid >> 8), *note_data);
				if ((*note_data)->next)
					(*note_data)->next->vol_lvl = get_volume(mdi, ((*note_data)->noteid >> 8), (*note_data)->next);
				note_data++;
			} while (note_data != mdi->last_note);
		}
	}

	if (options & WM_MO_REVERB) {
		for (i = 0; i < 4; i++) {
			mdi->filter.lowpass[i][0].in[0] = 0;
			mdi->filter.lowpass[i][0].in[1] = 0;
			mdi->filter.lowpass[i][1].in[0] = 0;
			mdi->filter.lowpass[i][1].in[1] = 0;

			mdi->filter.lowpass[i][0].out[0] = 0;
			mdi->filter.lowpass[i][0].out[1] = 0;
			mdi->filter.lowpass[i][1].out[0] = 0;
			mdi->filter.lowpass[i][1].out[1] = 0;

			mdi->filter.delay_pos[i][0] = 0;
			mdi->filter.delay_pos[i][1] = 0;

			memset (mdi->filter.delay[i][0], 0, (delay_size[i][0] * sizeof(signed long int)));
			memset (mdi->filter.delay[i][1], 0, (delay_size[i][1] * sizeof(signed long int)));
		}
	}
	WM_Unlock(&mdi->lock);
	return 0;
}

struct _WM_Info *
WildMidi_GetInfo (midi * handle) {
	struct _mdi *mdi = (struct _mdi *)handle;
	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return NULL;
	}
	if (handle == NULL) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0);
		return NULL;
	}
	WM_Lock(&mdi->lock);
	if (mdi->tmp_info == NULL) {
		mdi->tmp_info = malloc(sizeof(struct _WM_Info));
		if (mdi->tmp_info == NULL) {
			WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to set info", 0);
			WM_Unlock(&mdi->lock);
			return NULL;
		}
	}
	mdi->tmp_info->current_sample = mdi->info.current_sample;
	mdi->tmp_info->approx_total_samples = mdi->info.approx_total_samples;
	mdi->tmp_info->mixer_options = mdi->info.mixer_options;
	WM_Unlock(&mdi->lock);
	return mdi->tmp_info;
}

int
WildMidi_Shutdown ( void ) {
	struct _hndl * tmp_hdle;

	if (!WM_Initialized) {
		WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
		return -1;
	}
	if (first_handle != NULL) {
		while (first_handle != NULL) {
			tmp_hdle = first_handle->next;
			WildMidi_Close((struct _mdi *)first_handle->handle);
			free (first_handle);
			first_handle = tmp_hdle;
		}
	}
	WM_FreePatches();
	WM_Initialized = 0;
	return 0;
}

