shithub: dumb

Download patch

ref: 1eef4c9f892c119f2f92f0d928efe1b7a98af934
parent: 8605fb3a520aafb2d43febb6c0a9acde871090e7
author: Chris Moeller <kode54@gmail.com>
date: Thu Apr 25 20:25:04 EDT 2013

- Implemented Lanczos resampler - Fixed overhead of performing effects updates on background voices

--- a/dumb/include/dumb.h
+++ b/dumb/include/dumb.h
@@ -675,7 +675,7 @@
 typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
 
 #include "internal/blip_buf.h"
-#include "internal/fir_resampler.h"
+#include "internal/lanczos_resampler.h"
 
 struct DUMB_RESAMPLER
 {
--- /dev/null
+++ b/dumb/include/internal/lanczos_resampler.h
@@ -1,0 +1,18 @@
+#ifndef _LANCZOS_RESAMPLER_H_
+#define _LANCZOS_RESAMPLER_H_
+
+void lanczos_init();
+
+void * lanczos_resampler_create();
+void lanczos_resampler_delete(void *);
+void * lanczos_resampler_dup(void *);
+
+int lanczos_resampler_get_free_count(void *);
+void lanczos_resampler_write_sample(void *, short sample);
+void lanczos_resampler_set_rate( void *, double new_factor );
+int lanczos_resampler_ready(void *);
+void lanczos_resampler_clear(void *);
+int lanczos_resampler_get_sample(void *);
+void lanczos_resampler_remove_sample(void *);
+
+#endif
--- /dev/null
+++ b/dumb/src/helpers/lanczos_resampler.c
@@ -1,0 +1,216 @@
+#include <stdlib.h>
+#include <string.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#include "internal/lanczos_resampler.h"
+
+enum { COSINE_RESOLUTION = 8192 };
+enum { LANCZOS_RESOLUTION = 8192 };
+enum { LANCZOS_WIDTH = 8 };
+enum { LANCZOS_SAMPLES = LANCZOS_RESOLUTION * LANCZOS_WIDTH };
+
+static double cosine_lut[COSINE_RESOLUTION];
+static double lanczos_lut[LANCZOS_SAMPLES];
+
+enum { lanczos_buffer_size = LANCZOS_WIDTH * 4 };
+
+int fEqual(const double b, const double a)
+{
+	return fabs(a - b) < 1.0e-6;
+}
+
+static double sinc(double x)
+{
+	return fEqual(x, 0.0) ? 1.0 : sin(x * M_PI) / (x * M_PI);
+}
+
+void lanczos_init()
+{
+	unsigned i;
+	double dx, x;
+	for (i = 0; i < COSINE_RESOLUTION; ++i)
+		cosine_lut[i] = (1.0 - cos(((double)(i) / COSINE_RESOLUTION) * M_PI)) * 0.5;
+	dx = (double)(LANCZOS_WIDTH) / LANCZOS_SAMPLES; x = 0.0;
+	for (i = 0; i < LANCZOS_SAMPLES; ++i, x += dx)
+		lanczos_lut[i] = abs(x) < LANCZOS_WIDTH ? sinc(x) * (0.35875 + 0.48829 * cos((M_PI * x) / 3) + 0.14128 * cos((2 * M_PI * x) / 3) + 0.01168 * cos(M_PI * x)) : 0.0;
+}
+
+typedef struct lanczos_resampler
+{
+    int write_pos, write_filled;
+    int read_pos, read_filled;
+    unsigned short phase;
+    unsigned int phase_inc;
+    int buffer_in[lanczos_buffer_size * 2];
+    int buffer_out[lanczos_buffer_size];
+} lanczos_resampler;
+
+void * lanczos_resampler_create()
+{
+	lanczos_resampler * r = ( lanczos_resampler * ) malloc( sizeof(lanczos_resampler) );
+    if ( !r ) return 0;
+
+	r->write_pos = 0;
+    r->write_filled = 0;
+    r->read_pos = 0;
+    r->read_filled = 0;
+    r->phase = 0;
+    r->phase_inc = 0;
+    memset( r->buffer_in, 0, sizeof(r->buffer_in) );
+    memset( r->buffer_out, 0, sizeof(r->buffer_out) );
+
+	return r;
+}
+
+void lanczos_resampler_delete(void * _r)
+{
+	free( _r );
+}
+
+void * lanczos_resampler_dup(void * _r)
+{
+    lanczos_resampler * r_in = ( lanczos_resampler * ) _r;
+    lanczos_resampler * r_out = ( lanczos_resampler * ) malloc( sizeof(lanczos_resampler) );
+    if ( !r_out ) return 0;
+
+    r_out->write_pos = r_in->write_pos;
+    r_out->write_filled = r_in->write_filled;
+    r_out->read_pos = r_in->read_pos;
+    r_out->read_filled = r_in->read_filled;
+    r_out->phase = r_in->phase;
+    r_out->phase_inc = r_in->phase_inc;
+    memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) );
+    memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) );
+
+    return r_out;
+}
+
+int lanczos_resampler_get_free_count(void *_r)
+{
+	lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    return lanczos_buffer_size - r->write_filled;
+}
+
+int lanczos_resampler_ready(void *_r)
+{
+    lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    return r->write_filled > (LANCZOS_WIDTH * 2);
+}
+
+void lanczos_resampler_clear(void *_r)
+{
+    lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    r->write_pos = 0;
+    r->write_filled = 0;
+    r->read_pos = 0;
+    r->read_filled = 0;
+    r->phase = 0;
+    memset( r->buffer_in, 0, sizeof(r->buffer_in) );
+}
+
+void lanczos_resampler_set_rate(void *_r, double new_factor)
+{
+    lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    r->phase_inc = (int)( new_factor * LANCZOS_RESOLUTION );
+}
+
+void lanczos_resampler_write_sample(void *_r, short s)
+{
+	lanczos_resampler * r = ( lanczos_resampler * ) _r;
+
+    if ( r->write_filled < lanczos_buffer_size )
+	{
+        int s32 = s;
+
+        r->buffer_in[ r->write_pos ] = s32;
+        r->buffer_in[ r->write_pos + lanczos_buffer_size ] = s32;
+
+        ++r->write_filled;
+
+		r->write_pos = ( r->write_pos + 1 ) % lanczos_buffer_size;
+	}
+}
+
+int lanczos_resampler_run(void *_r, int ** out_, int * out_end)
+{
+	lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    int in_size = r->write_filled;
+    int const* in_ = r->buffer_in + lanczos_buffer_size + r->write_pos - r->write_filled;
+	int used = 0;
+	in_size -= LANCZOS_WIDTH * 2;
+	if ( in_size > 0 )
+	{
+		int* out = *out_;
+		int const* in = in_;
+		int const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+
+		int step = phase_inc > LANCZOS_RESOLUTION ? LANCZOS_RESOLUTION * LANCZOS_RESOLUTION / phase_inc : LANCZOS_RESOLUTION;
+		
+		do
+		{
+			// accumulate in extended precision
+            double kernel[LANCZOS_WIDTH * 2], kernel_sum = 0.0;
+			int i = LANCZOS_WIDTH;
+			double sample;
+
+			if ( out >= out_end )
+				break;
+
+			for (; i >= -LANCZOS_WIDTH + 1; --i)
+			{
+				int pos = i * step;
+				kernel_sum += kernel[i + LANCZOS_WIDTH - 1] = lanczos_lut[abs(phase - pos)];
+			}
+			for (sample = 0, i = 0; i < LANCZOS_WIDTH * 2; ++i)
+				sample += in[i] * kernel[i];
+			*out++ = (int) (sample / kernel_sum * 256.0);
+
+            phase += phase_inc;
+
+            in += phase >> 13;
+
+            phase &= 8191;
+		}
+		while ( in < in_end );
+		
+        r->phase = phase;
+		*out_ = out;
+
+		used = in - in_;
+
+        r->write_filled -= used;
+	}
+	
+	return used;
+}
+
+int lanczos_resampler_get_sample(void *_r)
+{
+    lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    if ( r->read_filled < 1 )
+    {
+        int write_pos = ( r->read_pos + r->read_filled ) % lanczos_buffer_size;
+        int write_size = lanczos_buffer_size - write_pos;
+        int * out = r->buffer_out + write_pos;
+        if ( write_size > ( lanczos_buffer_size - r->read_filled ) )
+            write_size = lanczos_buffer_size - r->read_filled;
+        lanczos_resampler_run( r, &out, out + write_size );
+        r->read_filled += out - r->buffer_out - write_pos;
+    }
+    if ( r->read_filled < 1 )
+        return 0;
+    return r->buffer_out[ r->read_pos ];
+}
+
+void lanczos_resampler_remove_sample(void *_r)
+{
+    lanczos_resampler * r = ( lanczos_resampler * ) _r;
+    if ( r->read_filled > 0 )
+    {
+        --r->read_filled;
+        r->read_pos = ( r->read_pos + 1 ) % lanczos_buffer_size;
+    }
+}
--- a/dumb/src/helpers/resamp3.inc
+++ b/dumb/src/helpers/resamp3.inc
@@ -191,13 +191,13 @@
                                     /* FIR resampling, backwards */
                                     SRCTYPE *x;
                                     if ( resampler->fir_resampler_ratio != delta ) {
-                                        fir_resampler_set_rate( resampler->fir_resampler[0], delta );
-                                        fir_resampler_set_rate( resampler->fir_resampler[1], delta );
+                                        lanczos_resampler_set_rate( resampler->fir_resampler[0], delta );
+                                        lanczos_resampler_set_rate( resampler->fir_resampler[1], delta );
                                         resampler->fir_resampler_ratio = delta;
                                     }
                                     x = &src[pos*SRC_CHANNELS];
                                     while ( todo ) {
-                                            while ( fir_resampler_get_free_count( resampler->fir_resampler[0] ) &&
+                                            while ( lanczos_resampler_get_free_count( resampler->fir_resampler[0] ) &&
                                                     pos >= resampler->start )
                                             {
                                                     POKE_FIR(0);
@@ -204,7 +204,7 @@
                                                     pos--;
                                                     x -= SRC_CHANNELS;
                                             }
-                                            if ( !fir_resampler_ready( resampler->fir_resampler[0] ) ) break;
+                                            if ( !lanczos_resampler_ready( resampler->fir_resampler[0] ) ) break;
                                             MIX_FIR;
                                             ADVANCE_FIR;
                                             --todo;
@@ -317,13 +317,13 @@
                                     /* FIR resampling, forwards */
                                     SRCTYPE *x;
                                     if ( resampler->fir_resampler_ratio != delta ) {
-                                        fir_resampler_set_rate( resampler->fir_resampler[0], delta );
-                                        fir_resampler_set_rate( resampler->fir_resampler[1], delta );
+                                        lanczos_resampler_set_rate( resampler->fir_resampler[0], delta );
+                                        lanczos_resampler_set_rate( resampler->fir_resampler[1], delta );
                                         resampler->fir_resampler_ratio = delta;
                                     }
                                     x = &src[pos*SRC_CHANNELS];
                                     while ( todo ) {
-                                            while ( fir_resampler_get_free_count( resampler->fir_resampler[0] ) &&
+                                            while ( lanczos_resampler_get_free_count( resampler->fir_resampler[0] ) &&
                                                     pos < resampler->end )
                                             {
                                                     POKE_FIR(0);
@@ -330,7 +330,7 @@
                                                     pos++;
                                                     x += SRC_CHANNELS;
                                             }
-                                            if ( !fir_resampler_ready( resampler->fir_resampler[0] ) ) break;
+                                            if ( !lanczos_resampler_ready( resampler->fir_resampler[0] ) ) break;
                                             MIX_FIR;
                                             ADVANCE_FIR;
                                             --todo;
--- a/dumb/src/helpers/resample.c
+++ b/dumb/src/helpers/resample.c
@@ -169,7 +169,7 @@
 		cubicA1[t] =  (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7)                 + (int)(1 << 14);
 	}
 
-    fir_init();
+    lanczos_init();
 }
 
 
--- a/dumb/src/helpers/resample.inc
+++ b/dumb/src/helpers/resample.inc
@@ -75,8 +75,8 @@
 	blip_clear(resampler->blip_buffer[0]);
 	blip_clear(resampler->blip_buffer[1]);
         resampler->fir_resampler_ratio = 0;
-        fir_resampler_clear(resampler->fir_resampler[0]);
-        fir_resampler_clear(resampler->fir_resampler[1]);
+        lanczos_resampler_clear(resampler->fir_resampler[0]);
+        lanczos_resampler_clear(resampler->fir_resampler[1]);
 }
 
 
@@ -153,15 +153,15 @@
 	resampler->last_clock += inv_dt; \
 }
 #define POKE_FIR(offset) { \
-        fir_resampler_write_sample( resampler->fir_resampler[0], FIR(x[offset]) ); \
+        lanczos_resampler_write_sample( resampler->fir_resampler[0], FIR(x[offset]) ); \
 }
 #define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol )
-#define MONO_DEST_PEEK_FIR *dst = MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), vol )
+#define MONO_DEST_PEEK_FIR *dst = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), vol )
 #define MONO_DEST_MIX_FIR { \
-        *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), vol ); \
+        *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), vol ); \
         UPDATE_VOLUME( volume, vol ); \
 }
-#define ADVANCE_FIR fir_resampler_remove_sample( resampler->fir_resampler[0] )
+#define ADVANCE_FIR lanczos_resampler_remove_sample( resampler->fir_resampler[0] )
 #define MONO_DEST_MIX_ALIAS(count) { \
 	int n = 0; \
 	resampler->last_clock -= count * 65536; \
@@ -179,12 +179,12 @@
 	*dst++ = MULSC( sample, rvol ); \
 }
 #define STEREO_DEST_PEEK_FIR { \
-        int sample = fir_resampler_get_sample( resampler->fir_resampler[0] ); \
+        int sample = lanczos_resampler_get_sample( resampler->fir_resampler[0] ); \
         *dst++ = MULSC( sample, lvol ); \
         *dst++ = MULSC( sample, rvol ); \
 }
 #define STEREO_DEST_MIX_FIR { \
-        int sample = fir_resampler_get_sample( resampler->fir_resampler[0] ); \
+        int sample = lanczos_resampler_get_sample( resampler->fir_resampler[0] ); \
         *dst++ += MULSC( sample, lvol ); \
         *dst++ += MULSC( sample, rvol ); \
         UPDATE_VOLUME( volume_left, lvol ); \
@@ -287,8 +287,8 @@
 	resampler->last_clock += inv_dt; \
 }
 #define POKE_FIR(offset) { \
-        fir_resampler_write_sample( resampler->fir_resampler[0], FIR(x[(offset)*2+0]) ); \
-        fir_resampler_write_sample( resampler->fir_resampler[1], FIR(x[(offset)*2+1]) ); \
+        lanczos_resampler_write_sample( resampler->fir_resampler[0], FIR(x[(offset)*2+0]) ); \
+        lanczos_resampler_write_sample( resampler->fir_resampler[1], FIR(x[(offset)*2+1]) ); \
 }
 #define MONO_DEST_PEEK_ALIAS { \
 	*dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \
@@ -295,18 +295,18 @@
 		MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
 }
 #define MONO_DEST_PEEK_FIR { \
-        *dst = MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \
-                MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+        *dst = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \
+                MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
 }
 #define MONO_DEST_MIX_FIR { \
-        *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \
-                MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+        *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \
+                MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
         UPDATE_VOLUME( volume_left, lvol ); \
         UPDATE_VOLUME( volume_right, rvol ); \
 }
 #define ADVANCE_FIR { \
-        fir_resampler_remove_sample( resampler->fir_resampler[0] ); \
-        fir_resampler_remove_sample( resampler->fir_resampler[1] ); \
+        lanczos_resampler_remove_sample( resampler->fir_resampler[0] ); \
+        lanczos_resampler_remove_sample( resampler->fir_resampler[1] ); \
 }
 #define MONO_DEST_MIX_ALIAS(count) { \
 	int n = 0; \
@@ -327,12 +327,12 @@
 	*dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \
 }
 #define STEREO_DEST_PEEK_FIR { \
-        *dst++ = MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
-        *dst++ = MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+        *dst++ = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
+        *dst++ = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
 }
 #define STEREO_DEST_MIX_FIR { \
-        *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
-        *dst++ += MULSC( fir_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
+        *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
+        *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \
         UPDATE_VOLUME( volume_left, lvol ); \
         UPDATE_VOLUME( volume_right, rvol ); \
 }
--- a/dumb/src/it/itrender.c
+++ b/dumb/src/it/itrender.c
@@ -51,7 +51,7 @@
 		blip_set_rates(r->resampler.blip_buffer[0], 65536, 1);
 		blip_set_rates(r->resampler.blip_buffer[1], 65536, 1);
         r->resampler.fir_resampler_ratio = 0.0;
-        r->resampler.fir_resampler[0] = fir_resampler_create();
+        r->resampler.fir_resampler[0] = lanczos_resampler_create();
         if ( !r->resampler.fir_resampler[0] )
         {
             free( r->resampler.blip_buffer[1] );
@@ -59,10 +59,10 @@
             free( r );
             return NULL;
         }
-        r->resampler.fir_resampler[1] = fir_resampler_create();
+        r->resampler.fir_resampler[1] = lanczos_resampler_create();
         if ( !r->resampler.fir_resampler[1] )
         {
-            fir_resampler_delete( r->resampler.fir_resampler[0] );
+            lanczos_resampler_delete( r->resampler.fir_resampler[0] );
             free( r->resampler.blip_buffer[1] );
             free( r->resampler.blip_buffer[0] );
             free( r );
@@ -74,8 +74,8 @@
 
 static void free_playing(IT_PLAYING * r)
 {
-    fir_resampler_delete( r->resampler.fir_resampler[1] );
-    fir_resampler_delete( r->resampler.fir_resampler[0] );
+    lanczos_resampler_delete( r->resampler.fir_resampler[1] );
+    lanczos_resampler_delete( r->resampler.fir_resampler[0] );
 	blip_delete( r->resampler.blip_buffer[1] );
 	blip_delete( r->resampler.blip_buffer[0] );
 	free( r );
@@ -184,7 +184,7 @@
 		return NULL;
 	}
     dst->resampler.fir_resampler_ratio = src->resampler.fir_resampler_ratio;
-    dst->resampler.fir_resampler[0] = fir_resampler_dup( src->resampler.fir_resampler[0] );
+    dst->resampler.fir_resampler[0] = lanczos_resampler_dup( src->resampler.fir_resampler[0] );
     if ( !dst->resampler.fir_resampler[0] )
     {
         blip_delete( dst->resampler.blip_buffer[1] );
@@ -192,10 +192,10 @@
         free( dst );
         return NULL;
     }
-    dst->resampler.fir_resampler[1] = fir_resampler_dup( src->resampler.fir_resampler[1] );
+    dst->resampler.fir_resampler[1] = lanczos_resampler_dup( src->resampler.fir_resampler[1] );
     if ( !dst->resampler.fir_resampler[1] )
     {
-        fir_resampler_delete( dst->resampler.fir_resampler[0] );
+        lanczos_resampler_delete( dst->resampler.fir_resampler[0] );
         blip_delete( dst->resampler.blip_buffer[1] );
         blip_delete( dst->resampler.blip_buffer[0] );
         free( dst );
@@ -305,7 +305,6 @@
 
 	dst->playing = dup_playing(src->playing, dst, src);
 
-
 #ifdef BIT_ARRAY_BULLSHIT
 	dst->played_patjump = bit_array_dup(src->played_patjump);
 	dst->played_patjump_order = src->played_patjump_order;
@@ -1223,7 +1222,47 @@
 }
 
 
+static void update_playing_effects(IT_PLAYING *playing)
+{
+	IT_CHANNEL *channel = playing->channel;
 
+	if (channel->channelvolslide) {
+		playing->channel_volume = channel->channelvolume;
+	}
+
+	if (channel->okt_toneslide) {
+		if (channel->okt_toneslide--) {
+			playing->note += channel->toneslide;
+			if (playing->note >= 120) {
+				if (channel->toneslide < 0) playing->note = 0;
+				else playing->note = 119;
+			}
+		}
+	} else if (channel->ptm_toneslide) {
+		if (--channel->toneslide_tick == 0) {
+			channel->toneslide_tick = channel->ptm_toneslide;
+			if (playing) {
+				playing->note += channel->toneslide;
+				if (playing->note >= 120) {
+					if (channel->toneslide < 0) playing->note = 0;
+					else playing->note = 119;
+				}
+				if (channel->playing == playing) {
+					channel->note = channel->truenote = playing->note;
+				}
+				if (channel->toneslide_retrig) {
+					it_playing_reset_resamplers(playing, 0);
+#ifdef END_RAMPING
+					playing->declick_stage = 0;
+					playing->declick_volume = 1.f / 256.f;
+#endif
+				}
+			}
+		}
+	}
+}
+
+
 static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer)
 {
 	int i, j;
@@ -1308,12 +1347,6 @@
 				else
 					channel->channelvolume = 0;
 			}
-			if (channel->playing)
-				channel->playing->channel_volume = channel->channelvolume;
-			for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) {
-				if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel)
-					sigrenderer->playing[j]->channel_volume = channel->channelvolume;
-			}
 		}
 
 		update_tremor(channel);
@@ -1324,128 +1357,67 @@
 
 		if (channel->inv_loop_speed) update_invert_loop(channel, playing ? playing->sample : NULL);
 
-		for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) {
-			if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) break;
-		}
+		if (playing) {
+			playing->slide += channel->portamento;
 
-		if (playing || j < DUMB_IT_N_NNA_CHANNELS) {
-			if (playing) playing->slide += channel->portamento;
-			for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) {
-				if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel)
-					sigrenderer->playing[j]->slide += channel->portamento;
-			}
-
-			if (channel->okt_toneslide) {
-				if (channel->okt_toneslide--) {
-					if (playing) {
-						playing->note += channel->toneslide;
-						if (playing->note >= 120) {
-							if (channel->toneslide < 0) playing->note = 0;
-							else playing->note = 119;
+			if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) {
+				if (channel->toneporta && channel->destnote < 120) {
+					int currpitch = ((playing->note - 60) << 8) + playing->slide;
+					int destpitch = (channel->destnote - 60) << 8;
+					if (currpitch > destpitch) {
+						currpitch -= channel->toneporta;
+						if (currpitch < destpitch) {
+							currpitch = destpitch;
+							channel->destnote = IT_NOTE_OFF;
 						}
-					}
-					for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) {
-						if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) {
-							IT_PLAYING *playing = sigrenderer->playing[j];
-							playing->note += channel->toneslide;
-							if (playing->note >= 120) {
-								if (channel->toneslide < 0) playing->note = 0;
-								else playing->note = 119;
-							}
+					} else if (currpitch < destpitch) {
+						currpitch += channel->toneporta;
+						if (currpitch > destpitch) {
+							currpitch = destpitch;
+							channel->destnote = IT_NOTE_OFF;
 						}
 					}
+					playing->slide = currpitch - ((playing->note - 60) << 8);
 				}
-			} else if (channel->ptm_toneslide) {
-				if (--channel->toneslide_tick == 0) {
-					channel->toneslide_tick = channel->ptm_toneslide;
-					if (playing) {
-						playing->note += channel->toneslide;
-						if (playing->note >= 120) {
-							if (channel->toneslide < 0) playing->note = 0;
-							else playing->note = 119;
-						}
-						channel->note = channel->truenote = playing->note;
-						if (channel->toneslide_retrig) {
-							it_playing_reset_resamplers(playing, 0);
-#ifdef END_RAMPING
-							playing->declick_stage = 0;
-							playing->declick_volume = 1.f / 256.f;
-#endif
-						}
-					}
-					for (j = 0; j < DUMB_IT_N_NNA_CHANNELS; j++) {
-						if (sigrenderer->playing[j] && sigrenderer->playing[j]->channel == channel) {
-							IT_PLAYING *playing = sigrenderer->playing[j];
-							playing->note += channel->toneslide;
-							if (playing->note >= 120) {
-								if (channel->toneslide < 0) playing->note = 0;
-								else playing->note = 119;
-							}
-							if (channel->toneslide_retrig) {
-								it_playing_reset_resamplers(playing, 0);
-#ifdef END_RAMPING
-								playing->declick_stage = 0;
-								playing->declick_volume = 1.f / 256.f;
-#endif
-							}
-						}
-					}
-				}
-			}
+			} else {
+				if (channel->toneporta && channel->destnote < 120) {
+					float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR);
 
-			if (playing) {
-				if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) {
-					if (channel->toneporta && channel->destnote < 120) {
-						int currpitch = ((playing->note - 60) << 8) + playing->slide;
-						int destpitch = (channel->destnote - 60) << 8;
-						if (currpitch > destpitch) {
-							currpitch -= channel->toneporta;
-							if (currpitch < destpitch) {
-								currpitch = destpitch;
-								channel->destnote = IT_NOTE_OFF;
-							}
-						} else if (currpitch < destpitch) {
-							currpitch += channel->toneporta;
-							if (currpitch > destpitch) {
-								currpitch = destpitch;
-								channel->destnote = IT_NOTE_OFF;
-							}
-						}
-						playing->slide = currpitch - ((playing->note - 60) << 8);
-					}
-				} else {
-					if (channel->toneporta && channel->destnote < 120) {
-						float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR);
+					float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note);
+					/* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */
 
-						float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note);
-						/* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */
+					float deltaslid = deltanote - playing->slide * amiga_multiplier;
 
-						float deltaslid = deltanote - playing->slide * amiga_multiplier;
-
-						float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote);
+					float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote);
+					if (deltaslid < destdelta) {
+						playing->slide -= channel->toneporta;
+						deltaslid = deltanote - playing->slide * amiga_multiplier;
+						if (deltaslid > destdelta) {
+							playing->note = channel->destnote;
+							playing->slide = 0;
+							channel->destnote = IT_NOTE_OFF;
+						}
+					} else {
+						playing->slide += channel->toneporta;
+						deltaslid = deltanote - playing->slide * amiga_multiplier;
 						if (deltaslid < destdelta) {
-							playing->slide -= channel->toneporta;
-							deltaslid = deltanote - playing->slide * amiga_multiplier;
-							if (deltaslid > destdelta) {
-								playing->note = channel->destnote;
-								playing->slide = 0;
-								channel->destnote = IT_NOTE_OFF;
-							}
-						} else {
-							playing->slide += channel->toneporta;
-							deltaslid = deltanote - playing->slide * amiga_multiplier;
-							if (deltaslid < destdelta) {
-								playing->note = channel->destnote;
-								playing->slide = 0;
-								channel->destnote = IT_NOTE_OFF;
-							}
+							playing->note = channel->destnote;
+							playing->slide = 0;
+							channel->destnote = IT_NOTE_OFF;
 						}
 					}
 				}
 			}
+
+			update_playing_effects(playing);
 		}
 	}
 
+	for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) {
+		IT_PLAYING *playing = sigrenderer->playing[i];
+		if (playing) update_playing_effects(playing);
+	}
+
 	update_smooth_effects(sigrenderer);
 }
 
@@ -1714,7 +1686,6 @@
 }
 
 
-
 static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel)
 {
 	IT_PLAYING_ENVELOPE volume_envelope;
@@ -1865,7 +1836,7 @@
 	if (channel->playing)
 		free_playing(channel->playing);
 
-    channel->playing = new_playing();
+    channel->playing = new_playing(channel);
 
 	if (!channel->playing)
 		return;
@@ -3505,7 +3476,7 @@
 			channel->destnote = IT_NOTE_OFF;
 
 			if (!channel->playing) {
-                channel->playing = new_playing();
+                channel->playing = new_playing(channel);
 				if (!channel->playing) {
 					if (playing) free_playing(playing);
 					return;
--- a/dumb/vc6/dumb/dumb.vcxproj
+++ b/dumb/vc6/dumb/dumb.vcxproj
@@ -108,6 +108,7 @@
     <ClCompile Include="..\..\src\helpers\blip_buf.c" />
     <ClCompile Include="..\..\src\helpers\clickrem.c" />
     <ClCompile Include="..\..\src\helpers\fir_resampler.c" />
+    <ClCompile Include="..\..\src\helpers\lanczos_resampler.c" />
     <ClCompile Include="..\..\src\helpers\lpc.c" />
     <ClCompile Include="..\..\src\helpers\memfile.c" />
     <ClCompile Include="..\..\src\helpers\resample.c" />
@@ -202,6 +203,7 @@
     <ClInclude Include="..\..\include\internal\dumb.h" />
     <ClInclude Include="..\..\include\internal\fir_resampler.h" />
     <ClInclude Include="..\..\include\internal\it.h" />
+    <ClInclude Include="..\..\include\internal\lanczos_resampler.h" />
     <ClInclude Include="..\..\include\internal\lpc.h" />
     <ClInclude Include="..\..\include\internal\riff.h" />
     <ClInclude Include="..\..\include\internal\stack_alloc.h" />
--- a/dumb/vc6/dumb/dumb.vcxproj.filters
+++ b/dumb/vc6/dumb/dumb.vcxproj.filters
@@ -282,6 +282,9 @@
     <ClCompile Include="..\..\src\it\readany2.c">
       <Filter>src\it\readers</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\src\helpers\lanczos_resampler.c">
+      <Filter>src\helpers</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\include\dumb.h">
@@ -309,6 +312,9 @@
       <Filter>include\internal</Filter>
     </ClInclude>
     <ClInclude Include="..\..\include\internal\blip_buf.h">
+      <Filter>include\internal</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\include\internal\lanczos_resampler.h">
       <Filter>include\internal</Filter>
     </ClInclude>
   </ItemGroup>