shithub: dumb

Download patch

ref: 9ac6cf69758fe0db6d6e654f298cd36efdb73366
parent: 4c0d2d92e6cc5c607a145f9fcbc8903e39327d1f
author: Chris Moeller <kode54@gmail.com>
date: Sun Jan 9 09:16:37 EST 2011

- Replaced old aliased resampling mode with a 65536x oversampling sinc resampler
- Version is now 0.9.9.23

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

--- a/dumb/include/dumb.h
+++ b/dumb/include/dumb.h
@@ -650,6 +650,8 @@
 
 typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
 
+#include "internal/blip_buf.h"
+
 struct DUMB_RESAMPLER
 {
 	void *src;
@@ -667,6 +669,9 @@
 		signed char x8[3*2];
 	} x;
 	int overshot;
+	int last_clock;
+	int last_amp[2];
+	blip_t* blip_buffer[2];
 };
 
 struct DUMB_VOLUME_RAMP_INFO
--- a/dumb/src/helpers/resamp2.inc
+++ b/dumb/src/helpers/resamp2.inc
@@ -94,7 +94,8 @@
 #define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES
 #define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES
 #define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
-#define MIX_ALIAS(op, upd, offset) MONO_DEST_MIX_ALIAS(op, upd, offset)
+#define MIX_ALIAS(count) MONO_DEST_MIX_ALIAS(count)
+#define PEEK_ALIAS MONO_DEST_PEEK_ALIAS
 #define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1)
 #define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
 #define MIX_ZEROS(op) *dst++ op 0
@@ -133,7 +134,8 @@
 	if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
 }
 #define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
-#define MIX_ALIAS(op, upd, offset) STEREO_DEST_MIX_ALIAS(op, upd, offset)
+#define MIX_ALIAS(count) STEREO_DEST_MIX_ALIAS(count)
+#define PEEK_ALIAS STEREO_DEST_PEEK_ALIAS
 #define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1)
 #define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3)
 #define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; }
@@ -153,6 +155,9 @@
 #undef MONO_DEST_VOLUME_ZEROS
 #undef MONO_DEST_VOLUME_VARIABLES
 #undef MONO_DEST_VOLUME_PARAMETERS
+#undef STEREO_DEST_PEEK_ALIAS
+#undef MONO_DEST_PEEK_ALIAS
+#undef POKE_ALIAS
 #undef COPYSRC2
 #undef COPYSRC
 #undef DIVIDE_BY_SRC_CHANNELS
--- a/dumb/src/helpers/resamp3.inc
+++ b/dumb/src/helpers/resamp3.inc
@@ -46,12 +46,13 @@
 
 long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta)
 {
-	int dt;
+	int dt, inv_dt;
 	int VOLUME_VARIABLES;
 	long done;
 	long todo;
 	LONG_LONG todo64;
 	int quality;
+	int blip_samples[256*SRC_CHANNELS];
 
 	if (!resampler || resampler->dir == 0) return 0;
 	ASSERT(resampler->dir == -1 || resampler->dir == 1);
@@ -59,6 +60,7 @@
 	done = 0;
 	dt = (int)(delta * 65536.0 + 0.5);
 	if (dt == 0 || dt == 0x80000000) return 0;
+	inv_dt = (int)(1.0 / delta * 65536.0 + 0.5);
 	SET_VOLUME_VARIABLES;
 
 	if (VOLUMES_ARE_ZERO) dst = NULL;
@@ -104,29 +106,34 @@
 					subpos = (long)new_subpos & 65535;
 				} else if (quality <= DUMB_RQ_ALIASING) {
 					/* Aliasing, backwards */
+					int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
 					SRCTYPE xbuf[2*SRC_CHANNELS];
 					SRCTYPE *x = &xbuf[0];
-					SRCTYPE *xstart;
 					COPYSRC(xbuf, 0, resampler->X, 1);
 					COPYSRC(xbuf, 1, resampler->X, 2);
-					while (todo && x < &xbuf[2*SRC_CHANNELS]) {
+					if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
+					while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) {
 						// TODO: check what happens when multiple tempo slides occur per row
 						HEAVYASSERT(pos >= resampler->start);
-						MIX_ALIAS(+=, 1, 0);
-						subpos += dt;
-						pos += subpos >> 16;
-						x -= (subpos >> 16) * SRC_CHANNELS;
-						subpos &= 65535;
-						todo--;
+						POKE_ALIAS(0);
+						pos--;
+						x += SRC_CHANNELS;
 					}
-					x = xstart = &src[pos*SRC_CHANNELS];
-					LOOP4(todo,
-						MIX_ALIAS(+=, 1, 2);
-						subpos += dt;
-						x += (subpos >> 16) * SRC_CHANNELS;
-						subpos &= 65535;
-					);
-					pos += DIVIDE_BY_SRC_CHANNELS(x - xstart);
+					x = &src[pos*SRC_CHANNELS];
+					while ( todo_clocks ) {
+						todo_clocks_set = todo_clocks;
+						if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
+						todo_clocks -= todo_clocks_set;
+						todo = ( todo_clocks_set - resampler->last_clock + inv_dt - 1 ) / inv_dt;
+						if ( todo < 0 ) todo = 0;
+						LOOP4(todo,
+							POKE_ALIAS(2);
+							pos--;
+							x -= SRC_CHANNELS;
+						);
+						todo = todo_clocks_set >> 16;
+						MIX_ALIAS( todo );
+					}
 				} else if (quality <= DUMB_RQ_LINEAR) {
 					/* Linear interpolation, backwards */
 					SRCTYPE xbuf[3*SRC_CHANNELS];
@@ -205,28 +212,33 @@
 					subpos = (long)new_subpos & 65535;
 				} else if (quality <= DUMB_RQ_ALIASING) {
 					/* Aliasing, forwards */
+					int todo_clocks = todo << 16, todo_clocks_set = todo_clocks;
 					SRCTYPE xbuf[2*SRC_CHANNELS];
 					SRCTYPE *x = &xbuf[0];
-					SRCTYPE *xstart;
 					COPYSRC(xbuf, 0, resampler->X, 1);
 					COPYSRC(xbuf, 1, resampler->X, 2);
-					while (todo && x < &xbuf[2*SRC_CHANNELS]) {
+					if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
+					while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) {
 						HEAVYASSERT(pos < resampler->end);
-						MIX_ALIAS(+=, 1, 0);
-						subpos += dt;
-						pos += subpos >> 16;
-						x += (subpos >> 16) * SRC_CHANNELS;
-						subpos &= 65535;
-						todo--;
+						POKE_ALIAS(0);
+						pos++;
+						x += SRC_CHANNELS;
 					}
-					x = xstart = &src[pos*SRC_CHANNELS];
-					LOOP4(todo,
-						MIX_ALIAS(+=, 1, -2);
-						subpos += dt;
-						x += (subpos >> 16) * SRC_CHANNELS;
-						subpos &= 65535;
-					);
-					pos += DIVIDE_BY_SRC_CHANNELS(x - xstart);
+					x = &src[pos*SRC_CHANNELS];
+					while ( todo_clocks ) {
+						todo_clocks_set = todo_clocks;
+						if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536;
+						todo_clocks -= todo_clocks_set;
+						todo = ( todo_clocks_set - resampler->last_clock + inv_dt - 1 ) / inv_dt;
+						if ( todo < 0 ) todo = 0;
+						LOOP4(todo,
+							POKE_ALIAS(-2);
+							pos++;
+							x += SRC_CHANNELS;
+						);
+						todo = todo_clocks_set >> 16;
+						MIX_ALIAS( todo );
+					}
 				} else if (quality <= DUMB_RQ_LINEAR) {
 					/* Linear interpolation, forwards */
 					SRCTYPE xbuf[3*SRC_CHANNELS];
@@ -339,7 +351,7 @@
 		HEAVYASSERT(pos >= resampler->start);
 		if (quality <= DUMB_RQ_ALIASING) {
 			/* Aliasing, backwards */
-			MIX_ALIAS(=, 0, 1);
+			PEEK_ALIAS;
 		} else if (quality <= DUMB_RQ_LINEAR) {
 			/* Linear interpolation, backwards */
 			MIX_LINEAR(=, 0, 2, 1);
@@ -351,7 +363,7 @@
 		HEAVYASSERT(pos < resampler->end);
 		if (quality <= DUMB_RQ_ALIASING) {
 			/* Aliasing */
-			MIX_ALIAS(=, 0, 1);
+			PEEK_ALIAS;
 		} else if (quality <= DUMB_RQ_LINEAR) {
 			/* Linear interpolation, forwards */
 			MIX_LINEAR(=, 0, 1, 2);
@@ -368,6 +380,7 @@
 #undef MIX_CUBIC
 #undef MIX_LINEAR
 #undef MIX_ALIAS
+#undef PEEK_ALIAS
 #undef VOLUMES_ARE_ZERO
 #undef SET_VOLUME_VARIABLES
 #undef RETURN_VOLUME_VARIABLES
--- a/dumb/src/helpers/resample.c
+++ b/dumb/src/helpers/resample.c
@@ -176,7 +176,7 @@
 
 #define SRCTYPE sample_t
 #define SRCBITS 24
-#define ALIAS(x, vol) MULSC(x, vol)
+#define ALIAS(x) (x >> 8)
 #define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos))
 /*
 #define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@@ -212,7 +212,7 @@
 #define SUFFIX _16
 #define SRCTYPE short
 #define SRCBITS 16
-#define ALIAS(x, vol) (x * vol >> 8)
+#define ALIAS(x) (x)
 #define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos))
 /*
 #define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
@@ -234,7 +234,7 @@
 #define SUFFIX _8
 #define SRCTYPE signed char
 #define SRCBITS 8
-#define ALIAS(x, vol) (x * vol)
+#define ALIAS(x) (x << 8)
 #define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
 /*
 #define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
--- a/dumb/src/helpers/resample.inc
+++ b/dumb/src/helpers/resample.inc
@@ -69,6 +69,11 @@
 	}
 	for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0;
 	resampler->overshot = -1;
+	resampler->last_clock = 0;
+	resampler->last_amp[0] = 0;
+	resampler->last_amp[1] = 0;
+	blip_clear(resampler->blip_buffer[0]);
+	blip_clear(resampler->blip_buffer[1]);
 }
 
 
@@ -77,6 +82,21 @@
 {
 	DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler));
 	if (!resampler) return NULL;
+	resampler->blip_buffer[0] = blip_new( 256 );
+	if (!resampler->blip_buffer[0])
+	{
+		free(resampler);
+		return NULL;
+	}
+	resampler->blip_buffer[1] = blip_new( 256 );
+	if (!resampler->blip_buffer[1])
+	{
+		free(resampler->blip_buffer[0]);
+		free(resampler);
+		return NULL;
+	}
+	blip_set_rates(resampler->blip_buffer[0], 65536, 1);
+	blip_set_rates(resampler->blip_buffer[1], 65536, 1);
 	dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality);
 	return resampler;
 }
@@ -123,17 +143,42 @@
 }
 #define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f
 #define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0)
-#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
-	*dst++ op ALIAS(x[offset], vol); \
-	if ( upd ) UPDATE_VOLUME( volume, vol ); \
+#define POKE_ALIAS(offset) { \
+	int delta = ALIAS(x[offset]) - resampler->last_amp[0]; \
+	resampler->last_amp[0] += delta; \
+	if ( delta ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, delta ); \
+	resampler->last_clock += inv_dt; \
 }
-#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \
-	int xm = x[offset]; \
-	*dst++ op ALIAS(xm, lvol); \
-	*dst++ op ALIAS(xm, rvol); \
-	if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
-	if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+#define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol )
+#define MONO_DEST_MIX_ALIAS(count) { \
+	int n = 0; \
+	resampler->last_clock -= count * 65536; \
+	blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
+	blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
+	LOOP4( count, \
+		*dst++ += MULSC( blip_samples[n], vol ); \
+		n++; \
+		UPDATE_VOLUME( volume, vol ); \
+	); \
 }
+#define STEREO_DEST_PEEK_ALIAS { \
+	int sample = blip_peek_sample( resampler->blip_buffer[0] ); \
+	*dst++ = MULSC( sample, lvol ); \
+	*dst++ = MULSC( sample, rvol ); \
+}
+#define STEREO_DEST_MIX_ALIAS(count) { \
+	int sample, n = 0; \
+	resampler->last_clock -= count * 65536; \
+	blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
+	blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
+	LOOP4( count, \
+		sample = blip_samples[n++]; \
+		*dst++ += MULSC( sample, lvol ); \
+		*dst++ += MULSC( sample, rvol ); \
+		UPDATE_VOLUME( volume_left, lvol ); \
+		UPDATE_VOLUME( volume_right, rvol ); \
+	); \
+}
 #define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
 	*dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \
 	if ( upd ) UPDATE_VOLUME( volume, vol ); \
@@ -208,16 +253,51 @@
 	if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \
 }
 #define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0)
-#define MONO_DEST_MIX_ALIAS(op, upd, offset) { \
-	*dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol); \
-	if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
-	if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+#define POKE_ALIAS(offset) { \
+	int deltal = ALIAS(x[(offset)*2+0]) - resampler->last_amp[0]; \
+	int deltar = ALIAS(x[(offset)*2+1]) - resampler->last_amp[1]; \
+	resampler->last_amp[0] += deltal; \
+	resampler->last_amp[1] += deltar; \
+	if ( deltal ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, deltal ); \
+	if ( deltar ) blip_add_delta( resampler->blip_buffer[1], resampler->last_clock, deltar ); \
+	resampler->last_clock += inv_dt; \
 }
-#define STEREO_DEST_MIX_ALIAS(op, upd, offset) { \
-	*dst++ op ALIAS(x[(offset)*2], lvol); \
-	*dst++ op ALIAS(x[(offset)*2+1], rvol); \
-	if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \
-	if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \
+#define MONO_DEST_PEEK_ALIAS { \
+	*dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \
+		MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
+}
+#define MONO_DEST_MIX_ALIAS(count) { \
+	int n = 0; \
+	resampler->last_clock -= count * 65536; \
+	blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
+	blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \
+	blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
+	blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \
+	LOOP4( count, \
+		*dst++ += MULSC( blip_samples[n], lvol ) + MULSC( blip_samples[256+n], rvol ); \
+		n++; \
+		UPDATE_VOLUME( volume_left, lvol ); \
+		UPDATE_VOLUME( volume_right, rvol ); \
+	); \
+}
+#define STEREO_DEST_PEEK_ALIAS { \
+	*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ); \
+	*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
+}
+#define STEREO_DEST_MIX_ALIAS(count) { \
+	int n = 0; \
+	resampler->last_clock -= count * 65536; \
+	blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \
+	blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \
+	blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \
+	blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \
+	LOOP4( count, \
+		*dst++ += MULSC( blip_samples[n], lvol); \
+		*dst++ += MULSC( blip_samples[256+n], rvol); \
+		n++; \
+		UPDATE_VOLUME( volume_left, lvol ); \
+		UPDATE_VOLUME( volume_right, rvol ); \
+	); \
 }
 #define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \
 	*dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \
--- a/dumb/src/it/itrender.c
+++ b/dumb/src/it/itrender.c
@@ -29,6 +29,37 @@
 #define END_RAMPING
 #define RAMP_DOWN
 
+static IT_PLAYING *new_playing()
+{
+	IT_PLAYING * r = (IT_PLAYING*) malloc(sizeof(*r));
+	if (r)
+	{
+		r->resampler.blip_buffer[0] = blip_new( 256 );
+		if ( !r->resampler.blip_buffer[0] )
+		{
+			free( r );
+			return NULL;
+		}
+		r->resampler.blip_buffer[1] = blip_new( 256 );
+		if ( !r->resampler.blip_buffer[1] )
+		{
+			free( r->resampler.blip_buffer[0] );
+			free( r );
+			return NULL;
+		}
+		blip_set_rates(r->resampler.blip_buffer[0], 65536, 1);
+		blip_set_rates(r->resampler.blip_buffer[1], 65536, 1);
+	}
+	return r;
+}
+
+static void free_playing(IT_PLAYING * r)
+{
+	blip_delete( r->resampler.blip_buffer[1] );
+	blip_delete( r->resampler.blip_buffer[0] );
+	free( r );
+}
+
 static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel)
 {
 	IT_PLAYING *dst;
@@ -118,6 +149,19 @@
 
 	dst->resampler = src->resampler;
 	dst->resampler.pickup_data = dst;
+	dst->resampler.blip_buffer[0] = blip_dup( dst->resampler.blip_buffer[0] );
+	if ( !dst->resampler.blip_buffer[0] )
+	{
+		free( dst );
+		return NULL;
+	}
+	dst->resampler.blip_buffer[1] = blip_dup( dst->resampler.blip_buffer[1] );
+	if ( !dst->resampler.blip_buffer[1] )
+	{
+		blip_delete( dst->resampler.blip_buffer[0] );
+		free( dst );
+		return NULL;
+	}
 	dst->time_lost = src->time_lost;
 
 	//dst->output = src->output;
@@ -1472,7 +1516,7 @@
 #ifdef RAMP_DOWN
 				channel->playing->declick_stage = 2;
 #else
-				free(channel->playing);
+				free_playing(channel->playing);
 				channel->playing = NULL;
 #endif
 				break;
@@ -1526,7 +1570,7 @@
 #ifdef RAMP_DOWN
 							playing->declick_stage = 2;
 #else
-							free(playing);
+							free_playing(playing);
 							sigrenderer->playing[i] = NULL;
 #endif
 							if (channel->playing == playing) channel->playing = NULL;
@@ -1561,9 +1605,9 @@
 	}
 
 	if (channel->playing)
-		free(channel->playing);
+		free_playing(channel->playing);
 
-	channel->playing = malloc(sizeof(*channel->playing));
+	channel->playing = new_playing();
 
 	if (!channel->playing)
 		return;
@@ -2372,7 +2416,7 @@
 #ifdef RAMP_DOWN
 												playing->declick_stage = 2;
 #else
-												free(playing);
+												free_playing(playing);
 												sigrenderer->playing[i] = NULL;
 #endif
 												if (channel->playing == playing) channel->playing = NULL;
@@ -2870,10 +2914,8 @@
 {
 	DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata;
 	IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel];
-	IT_PLAYING playing;
+	IT_PLAYING * playing = NULL;
 
-	playing.sample = 0;
-
 	if (entry->mask & IT_ENTRY_INSTRUMENT) {
 		int oldsample = channel->sample;
 		int oldvolume = channel->volume;
@@ -2882,7 +2924,8 @@
 		if (channel->playing &&
 			!((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) &&
 			!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0)) {
-			playing = *channel->playing;
+			playing = dup_playing(channel->playing, channel, channel);
+			if (!playing) return;
 			if (!(sigdata->flags & IT_WAS_A_MOD)) {
 				/* Retrigger vol/pan envelopes if enabled, and cancel fadeout.
 				 * Also reset vol/pan to that of _original_ instrument.
@@ -2911,21 +2954,20 @@
 					if (!channel->sample) {
 						if (channel->playing)
 						{
-							free(channel->playing);
+							free_playing(channel->playing);
 							channel->playing = NULL;
 						}
 					} else {
-						if (!channel->playing) {
-							channel->playing = malloc(sizeof(*channel->playing));
-							if (!channel->playing) return;
+						if (channel->playing) {
+							free_playing(channel->playing);
 						}
-						*channel->playing = playing;
-						playing.sample = (IT_SAMPLE *)-1;
+						channel->playing = playing;
+						playing = NULL;
 						channel->playing->declick_stage = 0;
 						channel->playing->declick_volume = 1.f / 256.f;
 #else
 					if (!channel->sample) {
-						free(channel->playing);
+						free_playing(channel->playing);
 						channel->playing = NULL;
 					} else {
 #endif
@@ -2966,7 +3008,11 @@
 			if (channel->playing) {
 #ifdef RAMP_DOWN
 				int i;
-				if (playing.sample) *channel->playing = playing;
+				if (playing) {
+					free_playing(channel->playing);
+					channel->playing = playing;
+					playing = NULL;
+				}
 				for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
 					if (!sigrenderer->playing[i]) {
 						channel->playing->declick_stage = 2;
@@ -2976,14 +3022,15 @@
 					}
 				}
 				if (channel->playing) {
-					free(channel->playing);
+					free_playing(channel->playing);
 					channel->playing = NULL;
 				}
 #else
-				free(channel->playing);
+				free_playing(channel->playing);
 				channel->playing = NULL;
 #endif
 			}
+			if (playing) free_playing(playing);
 			return;
 		} else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) {
 			/* Don't retrigger note; portamento in the volume column. */
@@ -2996,20 +3043,26 @@
 			channel->destnote = IT_NOTE_OFF;
 
 			if (!channel->playing) {
-				channel->playing = malloc(sizeof(*channel->playing));
-				if (!channel->playing)
+				channel->playing = new_playing();
+				if (!channel->playing) {
+					if (playing) free_playing(playing);
 					return;
+				}
 				// Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone.
 				retrigger_xm_envelopes(channel->playing);
 			}
 #ifdef RAMP_DOWN
-			else if (playing.sample != (IT_SAMPLE *)-1) {
+			else if (playing) {
 				/* volume rampy stuff! move note to NNA */
 				int i;
-				IT_PLAYING * ptemp = malloc(sizeof(*channel->playing));
-				if (!ptemp) return;
-				if (playing.sample) *ptemp = playing;
-				else *ptemp = *channel->playing;
+				IT_PLAYING * ptemp;
+				if (playing->sample) ptemp = playing;
+				else ptemp = channel->playing;
+				if (!ptemp) {
+					if (playing) free_playing(playing);
+					return;
+				}
+				playing = NULL;
 				for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
 					if (!sigrenderer->playing[i]) {
 						ptemp->declick_stage = 2;
@@ -3019,7 +3072,7 @@
 						break;
 					}
 				}
-				if (ptemp) free(ptemp);
+				if (ptemp) free_playing(ptemp);
 			}
 #endif
 
@@ -3170,6 +3223,8 @@
 				break;
 		}
 	}
+
+	if (playing) free_playing(playing);
 }
 
 
@@ -3247,11 +3302,11 @@
 						}
 					}
 					if (channel->playing) {
-						free(channel->playing);
+						free_playing(channel->playing);
 						channel->playing = NULL;
 					}
 #else
-					free(channel->playing);
+					free_playing(channel->playing);
 					channel->playing = NULL;
 #endif
 				}
@@ -3708,7 +3763,7 @@
 				//if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) {
 				// This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it.
 				if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) {
-					free(sigrenderer->channel[i].playing);
+					free_playing(sigrenderer->channel[i].playing);
 					sigrenderer->channel[i].playing = NULL;
 				}
 			}
@@ -3719,7 +3774,7 @@
 		if (sigrenderer->playing[i]) {
 			process_playing(sigrenderer, sigrenderer->playing[i], invt2g);
 			if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) {
-				free(sigrenderer->playing[i]);
+				free_playing(sigrenderer->playing[i]);
 				sigrenderer->playing[i] = NULL;
 			}
 		}
@@ -4728,7 +4783,7 @@
 			(sigrenderer->channel[i].playing->declick_stage == 3) || 
 #endif
 			(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) {
-				free(sigrenderer->channel[i].playing);
+				free_playing(sigrenderer->channel[i].playing);
 				sigrenderer->channel[i].playing = NULL;
 			}
 		}
@@ -4741,7 +4796,7 @@
 				(sigrenderer->playing[i]->declick_stage == 3) ||
 #endif
 				(sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) {
-				free(sigrenderer->playing[i]);
+				free_playing(sigrenderer->playing[i]);
 				sigrenderer->playing[i] = NULL;
 			}
 		}
@@ -5144,7 +5199,7 @@
 	if (sigrenderer) {
 		for (i = 0; i < DUMB_IT_N_CHANNELS; i++) {
 			if (sigrenderer->channel[i].playing)
-				free(sigrenderer->channel[i].playing);
+				free_playing(sigrenderer->channel[i].playing);
 #ifdef BIT_ARRAY_BULLSHIT
 			bit_array_destroy(sigrenderer->channel[i].played_patjump);
 #endif
@@ -5152,7 +5207,7 @@
 
 		for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++)
 			if (sigrenderer->playing[i])
-				free(sigrenderer->playing[i]);
+				free_playing(sigrenderer->playing[i]);
 
 		dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover);
 
--- a/dumb/vc6/dumb/dumb.vcxproj
+++ b/dumb/vc6/dumb/dumb.vcxproj
@@ -306,6 +306,7 @@
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
+    <ClCompile Include="..\..\src\helpers\blip_buf.c" />
     <ClCompile Include="..\..\src\helpers\clickrem.c">
       <Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -710,6 +711,7 @@
   <ItemGroup>
     <ClInclude Include="..\..\include\dumb.h" />
     <ClInclude Include="..\..\include\internal\barray.h" />
+    <ClInclude Include="..\..\include\internal\blip_buf.h" />
     <ClInclude Include="..\..\include\internal\dumb.h" />
     <ClInclude Include="..\..\include\internal\it.h" />
     <ClInclude Include="..\..\include\internal\riff.h" />
--- a/dumb/vc6/dumb/dumb.vcxproj.filters
+++ b/dumb/vc6/dumb/dumb.vcxproj.filters
@@ -249,6 +249,9 @@
     <ClCompile Include="..\..\src\it\xmeffect.c">
       <Filter>src\it</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\helpers\blip_buf.c">
+      <Filter>src\helpers</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\dumb.h">
@@ -266,12 +269,15 @@
     <ClInclude Include="..\..\include\internal\riff.h">
       <Filter>include\internal</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\include\internal\blip_buf.h">
+      <Filter>include\internal</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
-    <CustomBuild Include="..\..\src\helpers\resamp2.inc">
+    <CustomBuild Include="..\..\src\helpers\resamp3.inc">
       <Filter>src\helpers</Filter>
     </CustomBuild>
-    <CustomBuild Include="..\..\src\helpers\resamp3.inc">
+    <CustomBuild Include="..\..\src\helpers\resamp2.inc">
       <Filter>src\helpers</Filter>
     </CustomBuild>
     <CustomBuild Include="..\..\src\helpers\resample.inc">