shithub: sf2mid

Download patch

ref: 031e4fecc37f8dc59725127941ae3d1f64867ff0
parent: 6bebb187e491494b94fb69745fa6cddb4999e98a
author: Bernhard Schelling <14200249+schellingb@users.noreply.github.com>
date: Fri Mar 20 20:33:06 EDT 2020

Clamp all region values according to SF2 spec
Related to #46

--- a/tsf.h
+++ b/tsf.h
@@ -468,69 +468,173 @@
 	i->delayVibLFO = -12000.0f;
 }
 
-static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount)
+static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount, struct tsf_region* merge_region)
 {
 	enum
 	{
-		StartAddrsOffset, EndAddrsOffset, StartloopAddrsOffset, EndloopAddrsOffset, StartAddrsCoarseOffset, ModLfoToPitch, VibLfoToPitch, ModEnvToPitch,
-		InitialFilterFc, InitialFilterQ, ModLfoToFilterFc, ModEnvToFilterFc, EndAddrsCoarseOffset, ModLfoToVolume, Unused1, ChorusEffectsSend,
-		ReverbEffectsSend, Pan, Unused2, Unused3, Unused4, DelayModLFO, FreqModLFO, DelayVibLFO, FreqVibLFO, DelayModEnv, AttackModEnv, HoldModEnv,
-		DecayModEnv, SustainModEnv, ReleaseModEnv, KeynumToModEnvHold, KeynumToModEnvDecay, DelayVolEnv, AttackVolEnv, HoldVolEnv, DecayVolEnv,
-		SustainVolEnv, ReleaseVolEnv, KeynumToVolEnvHold, KeynumToVolEnvDecay, Instrument, Reserved1, KeyRange, VelRange, StartloopAddrsCoarseOffset,
-		Keynum, Velocity, InitialAttenuation, Reserved2, EndloopAddrsCoarseOffset, CoarseTune, FineTune, SampleID, SampleModes, Reserved3, ScaleTuning,
-		ExclusiveClass, OverridingRootKey, Unused5, EndOper
+		_GEN_TYPE_MASK       = 0x0F,
+		GEN_FLOAT            = 0x01,
+		GEN_INT              = 0x02,
+		GEN_UINT_ADD         = 0x03,
+		GEN_UINT_ADD15       = 0x04,
+		GEN_KEYRANGE         = 0x05,
+		GEN_VELRANGE         = 0x06,
+		GEN_LOOPMODE         = 0x07,
+		GEN_GROUP            = 0x08,
+		GEN_KEYCENTER        = 0x09,
+
+		_GEN_LIMIT_MASK      = 0xF0,
+		GEN_INT_LIMIT12K     = 0x10, //min -12000, max 12000
+		GEN_INT_LIMITFC      = 0x20, //min 1500, max 13500
+		GEN_INT_LIMITQ       = 0x30, //min 0, max 960
+		GEN_INT_LIMIT960     = 0x40, //min -960, max 960
+		GEN_INT_LIMIT16K4500 = 0x50, //min -16000, max 4500
+		GEN_FLOAT_LIMIT12K5K = 0x60, //min -12000, max 5000
+		GEN_FLOAT_LIMIT12K8K = 0x70, //min -12000, max 8000
+		GEN_FLOAT_LIMIT1200  = 0x80, //min -1200, max 1200
+		GEN_FLOAT_LIMITPAN   = 0x90, //* .001f, min -.5f, max .5f,
+		GEN_FLOAT_LIMITATTN  = 0xA0, //* .1f, min 0, max 144.0
+		GEN_FLOAT_MAX1000    = 0xB0, //min 0, max 1000
+		GEN_FLOAT_MAX1440    = 0xC0, //min 0, max 1440
+
+		_GEN_MAX = 59,
 	};
-	switch (genOper)
+	#define _TSFREGIONOFFSET(TYPE, FIELD) (unsigned char)(((TYPE*)&((struct tsf_region*)0)->FIELD) - (TYPE*)0)
+	#define _TSFREGIONENVOFFSET(TYPE, ENV, FIELD) (unsigned char)(((TYPE*)&((&(((struct tsf_region*)0)->ENV))->FIELD)) - (TYPE*)0)
+	static const struct { unsigned char mode, offset; } genMetas[_GEN_MAX] =
 	{
-		case StartAddrsOffset:           region->offset += amount->shortAmount; break;
-		case EndAddrsOffset:             region->end += amount->shortAmount; break;
-		case StartloopAddrsOffset:       region->loop_start += amount->shortAmount; break;
-		case EndloopAddrsOffset:         region->loop_end += amount->shortAmount; break;
-		case StartAddrsCoarseOffset:     region->offset += amount->shortAmount * 32768; break;
-		case ModLfoToPitch:              region->modLfoToPitch = amount->shortAmount; break;
-		case VibLfoToPitch:              region->vibLfoToPitch = amount->shortAmount; break;
-		case ModEnvToPitch:              region->modEnvToPitch = amount->shortAmount; break;
-		case InitialFilterFc:            region->initialFilterFc = amount->shortAmount; break;
-		case InitialFilterQ:             region->initialFilterQ = amount->shortAmount; break;
-		case ModLfoToFilterFc:           region->modLfoToFilterFc = amount->shortAmount; break;
-		case ModEnvToFilterFc:           region->modEnvToFilterFc = amount->shortAmount; break;
-		case EndAddrsCoarseOffset:       region->end += amount->shortAmount * 32768; break;
-		case ModLfoToVolume:             region->modLfoToVolume = amount->shortAmount; break;
-		case Pan:                        region->pan = amount->shortAmount / 1000.0f; break;
-		case DelayModLFO:                region->delayModLFO = amount->shortAmount; break;
-		case FreqModLFO:                 region->freqModLFO = amount->shortAmount; break;
-		case DelayVibLFO:                region->delayVibLFO = amount->shortAmount; break;
-		case FreqVibLFO:                 region->freqVibLFO = amount->shortAmount; break;
-		case DelayModEnv:                region->modenv.delay = amount->shortAmount; break;
-		case AttackModEnv:               region->modenv.attack = amount->shortAmount; break;
-		case HoldModEnv:                 region->modenv.hold = amount->shortAmount; break;
-		case DecayModEnv:                region->modenv.decay = amount->shortAmount; break;
-		case SustainModEnv:              region->modenv.sustain = amount->shortAmount; break;
-		case ReleaseModEnv:              region->modenv.release = amount->shortAmount; break;
-		case KeynumToModEnvHold:         region->modenv.keynumToHold = amount->shortAmount; break;
-		case KeynumToModEnvDecay:        region->modenv.keynumToDecay = amount->shortAmount; break;
-		case DelayVolEnv:                region->ampenv.delay = amount->shortAmount; break;
-		case AttackVolEnv:               region->ampenv.attack = amount->shortAmount; break;
-		case HoldVolEnv:                 region->ampenv.hold = amount->shortAmount; break;
-		case DecayVolEnv:                region->ampenv.decay = amount->shortAmount; break;
-		case SustainVolEnv:              region->ampenv.sustain = amount->shortAmount; break;
-		case ReleaseVolEnv:              region->ampenv.release = amount->shortAmount; break;
-		case KeynumToVolEnvHold:         region->ampenv.keynumToHold = amount->shortAmount; break;
-		case KeynumToVolEnvDecay:        region->ampenv.keynumToDecay = amount->shortAmount; break;
-		case KeyRange:                   region->lokey = amount->range.lo; region->hikey = amount->range.hi; break;
-		case VelRange:                   region->lovel = amount->range.lo; region->hivel = amount->range.hi; break;
-		case StartloopAddrsCoarseOffset: region->loop_start += amount->shortAmount * 32768; break;
-		case InitialAttenuation:         region->attenuation += amount->shortAmount * 0.1f; break;
-		case EndloopAddrsCoarseOffset:   region->loop_end += amount->shortAmount * 32768; break;
-		case CoarseTune:                 region->transpose += amount->shortAmount; break;
-		case FineTune:                   region->tune += amount->shortAmount; break;
-		case SampleModes:                region->loop_mode = ((amount->wordAmount&3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount&3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); break;
-		case ScaleTuning:                region->pitch_keytrack = amount->shortAmount; break;
-		case ExclusiveClass:             region->group = amount->wordAmount; break;
-		case OverridingRootKey:          region->pitch_keycenter = amount->shortAmount; break;
-		//case gen_endOper: break; // Ignore.
-		//default: addUnsupportedOpcode(generator_name);
+		{ GEN_UINT_ADD                     , _TSFREGIONOFFSET(unsigned int, offset               ) }, // 0 StartAddrsOffset
+		{ GEN_UINT_ADD                     , _TSFREGIONOFFSET(unsigned int, end                  ) }, // 1 EndAddrsOffset
+		{ GEN_UINT_ADD                     , _TSFREGIONOFFSET(unsigned int, loop_start           ) }, // 2 StartloopAddrsOffset
+		{ GEN_UINT_ADD                     , _TSFREGIONOFFSET(unsigned int, loop_end             ) }, // 3 EndloopAddrsOffset
+		{ GEN_UINT_ADD15                   , _TSFREGIONOFFSET(unsigned int, offset               ) }, // 4 StartAddrsCoarseOffset
+		{ GEN_INT   | GEN_INT_LIMIT12K     , _TSFREGIONOFFSET(         int, modLfoToPitch        ) }, // 5 ModLfoToPitch
+		{ GEN_INT   | GEN_INT_LIMIT12K     , _TSFREGIONOFFSET(         int, vibLfoToPitch        ) }, // 6 VibLfoToPitch
+		{ GEN_INT   | GEN_INT_LIMIT12K     , _TSFREGIONOFFSET(         int, modEnvToPitch        ) }, // 7 ModEnvToPitch
+		{ GEN_INT   | GEN_INT_LIMITFC      , _TSFREGIONOFFSET(         int, initialFilterFc      ) }, // 8 InitialFilterFc
+		{ GEN_INT   | GEN_INT_LIMITQ       , _TSFREGIONOFFSET(         int, initialFilterQ       ) }, // 9 InitialFilterQ
+		{ GEN_INT   | GEN_INT_LIMIT12K     , _TSFREGIONOFFSET(         int, modLfoToFilterFc     ) }, //10 ModLfoToFilterFc
+		{ GEN_INT   | GEN_INT_LIMIT12K     , _TSFREGIONOFFSET(         int, modEnvToFilterFc     ) }, //11 ModEnvToFilterFc
+		{ GEN_UINT_ADD15                   , _TSFREGIONOFFSET(unsigned int, end                  ) }, //12 EndAddrsCoarseOffset
+		{ GEN_INT   | GEN_INT_LIMIT960     , _TSFREGIONOFFSET(         int, modLfoToVolume       ) }, //13 ModLfoToVolume
+		{ 0                                , (0                                                  ) }, //   Unused
+		{ 0                                , (0                                                  ) }, //15 ChorusEffectsSend (unsupported)
+		{ 0                                , (0                                                  ) }, //16 ReverbEffectsSend (unsupported)
+		{ GEN_FLOAT | GEN_FLOAT_LIMITPAN   , _TSFREGIONOFFSET(       float, pan                  ) }, //17 Pan
+		{ 0                                , (0                                                  ) }, //   Unused
+		{ 0                                , (0                                                  ) }, //   Unused
+		{ 0                                , (0                                                  ) }, //   Unused
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET(       float, delayModLFO          ) }, //21 DelayModLFO
+		{ GEN_INT   | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET(         int, freqModLFO           ) }, //22 FreqModLFO
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET(       float, delayVibLFO          ) }, //23 DelayVibLFO
+		{ GEN_INT   | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET(         int, freqVibLFO           ) }, //24 FreqVibLFO
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET(    float, modenv, delay        ) }, //25 DelayModEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET(    float, modenv, attack       ) }, //26 AttackModEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET(    float, modenv, hold         ) }, //27 HoldModEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET(    float, modenv, decay        ) }, //28 DecayModEnv
+		{ GEN_FLOAT | GEN_FLOAT_MAX1000    , _TSFREGIONENVOFFSET(    float, modenv, sustain      ) }, //29 SustainModEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET(    float, modenv, release      ) }, //30 ReleaseModEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT1200  , _TSFREGIONENVOFFSET(    float, modenv, keynumToHold ) }, //31 KeynumToModEnvHold
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT1200  , _TSFREGIONENVOFFSET(    float, modenv, keynumToDecay) }, //32 KeynumToModEnvDecay
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET(    float, ampenv, delay        ) }, //33 DelayVolEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET(    float, ampenv, attack       ) }, //34 AttackVolEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET(    float, ampenv, hold         ) }, //35 HoldVolEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET(    float, ampenv, decay        ) }, //36 DecayVolEnv
+		{ GEN_FLOAT | GEN_FLOAT_MAX1440    , _TSFREGIONENVOFFSET(    float, ampenv, sustain      ) }, //37 SustainVolEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET(    float, ampenv, release      ) }, //38 ReleaseVolEnv
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT1200  , _TSFREGIONENVOFFSET(    float, ampenv, keynumToHold ) }, //39 KeynumToVolEnvHold
+		{ GEN_FLOAT | GEN_FLOAT_LIMIT1200  , _TSFREGIONENVOFFSET(    float, ampenv, keynumToDecay) }, //40 KeynumToVolEnvDecay
+		{ 0                                , (0                                                  ) }, //   Instrument (special)
+		{ 0                                , (0                                                  ) }, //   Reserved
+		{ GEN_KEYRANGE                     , (0                                                  ) }, //43 KeyRange
+		{ GEN_VELRANGE                     , (0                                                  ) }, //44 VelRange
+		{ GEN_UINT_ADD15                   , _TSFREGIONOFFSET(unsigned int, loop_start           ) }, //45 StartloopAddrsCoarseOffset
+		{ 0                                , (0                                                  ) }, //46 Keynum (special)
+		{ 0                                , (0                                                  ) }, //47 Velocity (special)
+		{ GEN_FLOAT | GEN_FLOAT_LIMITATTN  , _TSFREGIONOFFSET(       float, attenuation          ) }, //48 InitialAttenuation
+		{ 0                                , (0                                                  ) }, //   Reserved
+		{ GEN_UINT_ADD15                   , _TSFREGIONOFFSET(unsigned int, loop_end             ) }, //50 EndloopAddrsCoarseOffset
+		{ GEN_INT                          , _TSFREGIONOFFSET(         int, transpose            ) }, //51 CoarseTune
+		{ GEN_INT                          , _TSFREGIONOFFSET(         int, tune                 ) }, //52 FineTune
+		{ 0                                , (0                                                  ) }, //   SampleID (special)
+		{ GEN_LOOPMODE                     , _TSFREGIONOFFSET(         int, loop_mode            ) }, //54 SampleModes
+		{ 0                                , (0                                                  ) }, //   Reserved
+		{ GEN_INT                          , _TSFREGIONOFFSET(         int, pitch_keytrack       ) }, //56 ScaleTuning
+		{ GEN_GROUP                        , _TSFREGIONOFFSET(unsigned int, group                ) }, //57 ExclusiveClass
+		{ GEN_KEYCENTER                    , _TSFREGIONOFFSET(         int, pitch_keycenter      ) }, //58 OverridingRootKey
+	};
+	#undef _TSFREGIONOFFSET
+	#undef _TSFREGIONENVOFFSET
+	if (amount)
+	{
+		int offset;
+		if (genOper >= _GEN_MAX) return;
+		offset = genMetas[genOper].offset;
+		switch (genMetas[genOper].mode & _GEN_TYPE_MASK)
+		{
+			case GEN_FLOAT:      ((       float*)region)[offset]  = amount->shortAmount;     return;
+			case GEN_INT:        ((         int*)region)[offset]  = amount->shortAmount;     return;
+			case GEN_UINT_ADD:   ((unsigned int*)region)[offset] += amount->shortAmount;     return;
+			case GEN_UINT_ADD15: ((unsigned int*)region)[offset] += amount->shortAmount<<15; return;
+			case GEN_KEYRANGE:   region->lokey = amount->range.lo; region->hikey = amount->range.hi; return;
+			case GEN_VELRANGE:   region->lovel = amount->range.lo; region->hivel = amount->range.hi; return;
+			case GEN_LOOPMODE:   region->loop_mode       = ((amount->wordAmount&3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount&3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); return;
+			case GEN_GROUP:      region->group           = amount->wordAmount;  return;
+			case GEN_KEYCENTER:  region->pitch_keycenter = amount->shortAmount; return;
+		}
 	}
+	else //merge regions and clamp values
+	{
+		for (genOper = 0; genOper != _GEN_MAX; genOper++)
+		{
+			int offset = genMetas[genOper].offset;
+			switch (genMetas[genOper].mode & _GEN_TYPE_MASK)
+			{
+				case GEN_FLOAT:
+				{
+					float *val = &((float*)region)[offset], vfactor, vmin, vmax;
+					*val += ((float*)merge_region)[offset];
+					switch (genMetas[genOper].mode & _GEN_LIMIT_MASK)
+					{
+						case GEN_FLOAT_LIMIT12K5K: vfactor =   1.0f; vmin = -12000.0f; vmax = 5000.0f; break;
+						case GEN_FLOAT_LIMIT12K8K: vfactor =   1.0f; vmin = -12000.0f; vmax = 8000.0f; break;
+						case GEN_FLOAT_LIMIT1200:  vfactor =   1.0f; vmin =  -1200.0f; vmax = 1200.0f; break;
+						case GEN_FLOAT_LIMITPAN:   vfactor = 0.001f; vmin =     -0.5f; vmax =    0.5f; break;
+						case GEN_FLOAT_LIMITATTN:  vfactor =   0.1f; vmin =      0.0f; vmax =  144.0f; break;
+						case GEN_FLOAT_MAX1000:    vfactor =   1.0f; vmin =      0.0f; vmax = 1000.0f; break;
+						case GEN_FLOAT_MAX1440:    vfactor =   1.0f; vmin =      0.0f; vmax = 1440.0f; break;
+						default: continue;
+					}
+					*val *= vfactor;
+					if      (*val < vmin) *val = vmin;
+					else if (*val > vmax) *val = vmax;
+					continue;
+				}
+				case GEN_INT:
+				{
+					int *val = &((int*)region)[offset], vmin, vmax;
+					*val += ((int*)merge_region)[offset];
+					switch (genMetas[genOper].mode & _GEN_LIMIT_MASK)
+					{
+						case GEN_INT_LIMIT12K:     vmin = -12000; vmax = 12000; break;
+						case GEN_INT_LIMITFC:      vmin =   1500; vmax = 13500; break;
+						case GEN_INT_LIMITQ:       vmin =      0; vmax =   960; break;
+						case GEN_INT_LIMIT960:     vmin =   -960; vmax =   960; break;
+						case GEN_INT_LIMIT16K4500: vmin = -16000; vmax =  4500; break;
+						default: continue;
+					}
+					if      (*val < vmin) *val = vmin;
+					else if (*val > vmax) *val = vmax;
+					continue;
+				}
+				case GEN_UINT_ADD:
+				{
+					((unsigned int*)region)[offset] += ((unsigned int*)merge_region)[offset];
+					continue;
+				}
+			}
+		}
+	}
 }
 
 static void tsf_region_envtosecs(struct tsf_envelope* p, TSF_BOOL sustainIsGain)
@@ -647,39 +751,7 @@
 								if (presetRegion.hivel < zoneRegion.hivel) zoneRegion.hivel = presetRegion.hivel;
 
 								//sum regions
-								zoneRegion.offset += presetRegion.offset;
-								zoneRegion.end += presetRegion.end;
-								zoneRegion.loop_start += presetRegion.loop_start;
-								zoneRegion.loop_end += presetRegion.loop_end;
-								zoneRegion.transpose += presetRegion.transpose;
-								zoneRegion.tune += presetRegion.tune;
-								zoneRegion.pitch_keytrack += presetRegion.pitch_keytrack;
-								zoneRegion.attenuation += presetRegion.attenuation;
-								zoneRegion.pan += presetRegion.pan;
-								zoneRegion.ampenv.delay += presetRegion.ampenv.delay;
-								zoneRegion.ampenv.attack += presetRegion.ampenv.attack;
-								zoneRegion.ampenv.hold += presetRegion.ampenv.hold;
-								zoneRegion.ampenv.decay += presetRegion.ampenv.decay;
-								zoneRegion.ampenv.sustain += presetRegion.ampenv.sustain;
-								zoneRegion.ampenv.release += presetRegion.ampenv.release;
-								zoneRegion.modenv.delay += presetRegion.modenv.delay;
-								zoneRegion.modenv.attack += presetRegion.modenv.attack;
-								zoneRegion.modenv.hold += presetRegion.modenv.hold;
-								zoneRegion.modenv.decay += presetRegion.modenv.decay;
-								zoneRegion.modenv.sustain += presetRegion.modenv.sustain;
-								zoneRegion.modenv.release += presetRegion.modenv.release;
-								zoneRegion.initialFilterQ += presetRegion.initialFilterQ;
-								zoneRegion.initialFilterFc += presetRegion.initialFilterFc;
-								zoneRegion.modEnvToPitch += presetRegion.modEnvToPitch;
-								zoneRegion.modEnvToFilterFc += presetRegion.modEnvToFilterFc;
-								zoneRegion.delayModLFO += presetRegion.delayModLFO;
-								zoneRegion.freqModLFO += presetRegion.freqModLFO;
-								zoneRegion.modLfoToPitch += presetRegion.modLfoToPitch;
-								zoneRegion.modLfoToFilterFc += presetRegion.modLfoToFilterFc;
-								zoneRegion.modLfoToVolume += presetRegion.modLfoToVolume;
-								zoneRegion.delayVibLFO += presetRegion.delayVibLFO;
-								zoneRegion.freqVibLFO += presetRegion.freqVibLFO;
-								zoneRegion.vibLfoToPitch += presetRegion.vibLfoToPitch;
+								tsf_region_operator(&zoneRegion, 0, TSF_NULL, &presetRegion);
 
 								// EG times need to be converted from timecents to seconds.
 								tsf_region_envtosecs(&zoneRegion.ampenv, TSF_TRUE);
@@ -689,13 +761,7 @@
 								zoneRegion.delayModLFO = (zoneRegion.delayModLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayModLFO));
 								zoneRegion.delayVibLFO = (zoneRegion.delayVibLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayVibLFO));
 
-								// Pin values to their ranges.
-								if (zoneRegion.pan < -0.5f) zoneRegion.pan = -0.5f;
-								else if (zoneRegion.pan > 0.5f) zoneRegion.pan = 0.5f;
-								if (zoneRegion.initialFilterQ < 1500 || zoneRegion.initialFilterQ > 13500) zoneRegion.initialFilterQ = 0;
-								if (zoneRegion.attenuation < 0) zoneRegion.attenuation = 0;
-								else if (zoneRegion.attenuation > 1440) zoneRegion.attenuation = 1440;
-
+								// Fixup sample positions
 								pshdr = &hydra->shdrs[pigen->genAmount.wordAmount];
 								zoneRegion.offset += pshdr->start;
 								zoneRegion.end += pshdr->end;
@@ -712,7 +778,7 @@
 								region_index++;
 								hadSampleID = 1;
 							}
-							else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount);
+							else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount, TSF_NULL);
 						}
 
 						// Handle instrument's global zone.
@@ -724,7 +790,7 @@
 					}
 					hadGenInstrument = 1;
 				}
-				else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount);
+				else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount, TSF_NULL);
 			}
 
 			// Modulators (TODO)
@@ -997,7 +1063,7 @@
 		if (dynamicLowpass)
 		{
 			float fres = tmpInitialFilterFc + v->modlfo.level * tmpModLfoToFilterFc + v->modenv.level * tmpModEnvToFilterFc;
-			float lowpassFc = (fres <= 13500 ? tsf_cents2Hertz(fres) / f->outSampleRate : 1.0f);
+			float lowpassFc = (fres <= 13500 ? tsf_cents2Hertz(fres) / tmpSampleRate : 1.0f);
 			tmpLowpass.active = (lowpassFc < 0.499f);
 			if (tmpLowpass.active) tsf_voice_lowpass_setup(&tmpLowpass, lowpassFc);
 		}
@@ -1011,8 +1077,8 @@
 		gainMono = noteGain * v->ampenv.level;
 
 		// Update EG.
-		tsf_voice_envelope_process(&v->ampenv, blockSamples, f->outSampleRate);
-		if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, f->outSampleRate);
+		tsf_voice_envelope_process(&v->ampenv, blockSamples, tmpSampleRate);
+		if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, tmpSampleRate);
 
 		// Update LFOs.
 		if (updateModLFO) tsf_voice_lfo_process(&v->modlfo, blockSamples);