shithub: dumb

Download patch

ref: 80e1e1a33c3b1b64012ef24a6537176ea0cc7fd0
parent: 8ea3282ceaf9e2915d60310fc74fed6951e2b754
author: Chris Moeller <kode54@gmail.com>
date: Sat Jan 10 16:50:15 EST 2015

Overhauled resampler quite a bit, and implmented a new band-limited linear interpolation mode

--- a/dumb/include/dumb.h
+++ b/dumb/include/dumb.h
@@ -676,9 +676,10 @@
 #define DUMB_RQ_ALIASING 0
 #define DUMB_RQ_BLEP     1
 #define DUMB_RQ_LINEAR   2
-#define DUMB_RQ_CUBIC    3
-#define DUMB_RQ_FIR      4
-#define DUMB_RQ_N_LEVELS 5
+#define DUMB_RQ_BLAM     3
+#define DUMB_RQ_CUBIC    4
+#define DUMB_RQ_FIR      5
+#define DUMB_RQ_N_LEVELS 6
 
 extern int dumb_resampling_quality; /* This specifies the default */
 void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality); /* This overrides it */
--- a/dumb/include/internal/resampler.h
+++ b/dumb/include/internal/resampler.h
@@ -36,9 +36,10 @@
     RESAMPLER_QUALITY_ZOH = 0,
     RESAMPLER_QUALITY_BLEP = 1,
     RESAMPLER_QUALITY_LINEAR = 2,
-    RESAMPLER_QUALITY_CUBIC = 3,
-    RESAMPLER_QUALITY_SINC = 4,
-    RESAMPLER_QUALITY_MAX = 4
+    RESAMPLER_QUALITY_BLAM = 3,
+    RESAMPLER_QUALITY_CUBIC = 4,
+    RESAMPLER_QUALITY_SINC = 5,
+    RESAMPLER_QUALITY_MAX = 5
 };
 
 void resampler_set_quality(void *, int quality);
@@ -52,6 +53,6 @@
 int resampler_get_sample_count(void *);
 int resampler_get_sample(void *);
 float resampler_get_sample_float(void *);
-void resampler_remove_sample(void *);
+void resampler_remove_sample(void *, int decay);
 
 #endif
--- a/dumb/src/helpers/resamp3.inc
+++ b/dumb/src/helpers/resamp3.inc
@@ -114,8 +114,12 @@
                                     }
                                     x = &src[pos*SRC_CHANNELS];
                                     while ( todo ) {
-                                            while ( resampler_get_free_count( resampler->fir_resampler[0] ) &&
-                                                    pos >= resampler->start )
+                                            while ( ( resampler_get_free_count( resampler->fir_resampler[0] ) ||
+                                            (!resampler_get_sample_count( resampler->fir_resampler[0] )
+#if SRC_CHANNELS == 2
+                                            && !resampler_get_sample_count( resampler->fir_resampler[1] )
+#endif
+                                            ) ) && pos >= resampler->start )
                                             {
                                                     POKE_FIR(0);
                                                     pos--;
@@ -159,8 +163,12 @@
                                     }
                                     x = &src[pos*SRC_CHANNELS];
                                     while ( todo ) {
-                                            while ( resampler_get_free_count( resampler->fir_resampler[0] ) &&
-                                                    pos < resampler->end )
+                                            while ( ( resampler_get_free_count( resampler->fir_resampler[0] ) ||
+                                            (!resampler_get_sample_count( resampler->fir_resampler[0] )
+#if SRC_CHANNELS == 2
+                                            && !resampler_get_sample_count( resampler->fir_resampler[1] )
+#endif
+                                            ) ) && pos < resampler->end )
                                             {
                                                     POKE_FIR(0);
                                                     pos++;
--- a/dumb/src/helpers/resample.c
+++ b/dumb/src/helpers/resample.c
@@ -74,13 +74,14 @@
  *  0 - DUMB_RQ_ALIASING - fastest
  *  1 - DUMB_RQ_BLEP     - nicer than aliasing, but slower
  *  2 - DUMB_RQ_LINEAR
- *  3 - DUMB_RQ_CUBIC
- *  4 - DUMB_RQ_FIR      - nicest
+ *  3 - DUMB_RQ_BLAM     - band-limited linear interpolation, nice but slower
+ *  4 - DUMB_RQ_CUBIC
+ *  5 - DUMB_RQ_FIR      - nicest
  *
  * Values outside the range 0-4 will behave the same as the nearest
  * value within the range.
  */
-int dumb_resampling_quality = DUMB_RQ_CUBIC;
+int dumb_resampling_quality = DUMB_RQ_BLAM;
 
 
 
--- a/dumb/src/helpers/resample.inc
+++ b/dumb/src/helpers/resample.inc
@@ -141,7 +141,7 @@
         *dst++ += MULSC( resampler_get_sample( resampler->fir_resampler[0] ), vol ); \
         UPDATE_VOLUME( volume, vol ); \
 }
-#define ADVANCE_FIR resampler_remove_sample( resampler->fir_resampler[0] )
+#define ADVANCE_FIR resampler_remove_sample( resampler->fir_resampler[0], 1 )
 #define STEREO_DEST_PEEK_FIR { \
         int sample = resampler_get_sample( resampler->fir_resampler[0] ); \
         *dst++ = MULSC( sample, lvol ); \
@@ -225,8 +225,8 @@
         UPDATE_VOLUME( volume_right, rvol ); \
 }
 #define ADVANCE_FIR { \
-        resampler_remove_sample( resampler->fir_resampler[0] ); \
-        resampler_remove_sample( resampler->fir_resampler[1] ); \
+        resampler_remove_sample( resampler->fir_resampler[0], 1 ); \
+        resampler_remove_sample( resampler->fir_resampler[1], 1 ); \
 }
 #define STEREO_DEST_PEEK_FIR { \
         *dst++ = MULSC( resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \
--- a/dumb/src/helpers/resampler.c
+++ b/dumb/src/helpers/resampler.c
@@ -6,6 +6,13 @@
 #include <xmmintrin.h>
 #define RESAMPLER_SSE
 #endif
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#if TARGET_CPU_ARM
+#include <arm_neon.h>
+#define RESAMPLER_NEON
+#endif
+#endif
 
 #ifdef _MSC_VER
 #define ALIGNED     _declspec(align(16))
@@ -27,6 +34,10 @@
 enum { SINC_SAMPLES = RESAMPLER_RESOLUTION * SINC_WIDTH };
 enum { CUBIC_SAMPLES = RESAMPLER_RESOLUTION * 4 };
 
+static const float RESAMPLER_BLEP_CUTOFF = 0.90f;
+static const float RESAMPLER_BLAM_CUTOFF = 0.93f;
+static const float RESAMPLER_SINC_CUTOFF = 0.999f;
+
 ALIGNED static float cubic_lut[CUBIC_SAMPLES];
 
 static float sinc_lut[SINC_SAMPLES + 1];
@@ -129,10 +140,10 @@
 {
     int write_pos, write_filled;
     int read_pos, read_filled;
-    unsigned int phase;
-    unsigned int phase_inc;
-    unsigned int inv_phase;
-    unsigned int inv_phase_inc;
+    float phase;
+    float phase_inc;
+    float inv_phase;
+    float inv_phase_inc;
     unsigned char quality;
     signed char delay_added;
     signed char delay_removed;
@@ -173,25 +184,10 @@
 
 void * resampler_dup(const void * _r)
 {
-    const resampler * r_in = ( const resampler * ) _r;
-    resampler * r_out = ( resampler * ) malloc( sizeof(resampler) );
+    void * r_out = malloc( sizeof(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;
-    r_out->inv_phase = r_in->inv_phase;
-    r_out->inv_phase_inc = r_in->inv_phase_inc;
-    r_out->quality = r_in->quality;
-    r_out->delay_added = r_in->delay_added;
-    r_out->delay_removed = r_in->delay_removed;
-    r_out->last_amp = r_in->last_amp;
-    r_out->accumulator = r_in->accumulator;
-    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) );
+    resampler_dup_inplace(r_out, _r);
 
     return r_out;
 }
@@ -227,7 +223,8 @@
         quality = RESAMPLER_QUALITY_MAX;
     if ( r->quality != quality )
     {
-        if ( quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLEP )
+        if ( quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLEP ||
+             quality == RESAMPLER_QUALITY_BLAM || r->quality == RESAMPLER_QUALITY_BLAM )
         {
             r->read_pos = 0;
             r->read_filled = 0;
@@ -257,6 +254,7 @@
         return 1;
             
     case RESAMPLER_QUALITY_LINEAR:
+    case RESAMPLER_QUALITY_BLAM:
         return 2;
             
     case RESAMPLER_QUALITY_CUBIC:
@@ -275,6 +273,7 @@
     case RESAMPLER_QUALITY_ZOH:
     case RESAMPLER_QUALITY_BLEP:
     case RESAMPLER_QUALITY_LINEAR:
+    case RESAMPLER_QUALITY_BLAM:
         return 0;
             
     case RESAMPLER_QUALITY_CUBIC:
@@ -297,6 +296,7 @@
         return 0;
             
     case RESAMPLER_QUALITY_BLEP:
+    case RESAMPLER_QUALITY_BLAM:
         return SINC_WIDTH - 1;
     }
 }
@@ -319,16 +319,19 @@
     r->delay_removed = -1;
     memset(r->buffer_in, 0, (SINC_WIDTH - 1) * sizeof(r->buffer_in[0]));
     memset(r->buffer_in + resampler_buffer_size, 0, (SINC_WIDTH - 1) * sizeof(r->buffer_in[0]));
-    if (r->quality == RESAMPLER_QUALITY_BLEP)
+    if (r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM)
+    {
+        r->inv_phase = 0;
         memset(r->buffer_out, 0, sizeof(r->buffer_out));
+    }
 }
 
 void resampler_set_rate(void *_r, double new_factor)
 {
     resampler * r = ( resampler * ) _r;
-    r->phase_inc = (int)( new_factor * RESAMPLER_RESOLUTION_EXTRA );
+    r->phase_inc = new_factor;
     new_factor = 1.0 / new_factor;
-    r->inv_phase_inc = (int)( new_factor * RESAMPLER_RESOLUTION_EXTRA );
+    r->inv_phase_inc = new_factor;
 }
 
 void resampler_write_sample(void *_r, short s)
@@ -390,8 +393,8 @@
         float* out = *out_;
         float const* in = in_;
         float const* const in_end = in + in_size;
-        int phase = r->phase;
-        int phase_inc = r->phase_inc;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
         
         do
         {
@@ -405,9 +408,9 @@
             
             phase += phase_inc;
             
-            in += phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            in += (int)phase;
             
-            phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            phase = fmod(phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -422,6 +425,7 @@
     return used;
 }
 
+#ifndef RESAMPLER_NEON
 static int resampler_run_blep(resampler * r, float ** out_, float * out_end)
 {
     int in_size = r->write_filled;
@@ -434,38 +438,45 @@
         float const* in = in_;
         float const* const in_end = in + in_size;
         float last_amp = r->last_amp;
-        int inv_phase = r->inv_phase;
-        int inv_phase_inc = r->inv_phase_inc;
+        float inv_phase = r->inv_phase;
+        float inv_phase_inc = r->inv_phase_inc;
         
-        const int step = RESAMPLER_RESOLUTION;
+        const int step = RESAMPLER_BLEP_CUTOFF * RESAMPLER_RESOLUTION;
+        const int window_step = RESAMPLER_RESOLUTION;
         
         do
         {
-            float kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
-            int phase_reduced = inv_phase >> RESAMPLER_SHIFT_EXTRA;
-            int i = SINC_WIDTH;
             float sample;
             
             if ( out + SINC_WIDTH * 2 > out_end )
                 break;
             
-            for (; i >= -SINC_WIDTH + 1; --i)
+            sample = *in++ - last_amp;
+            
+            if (sample)
             {
-                int pos = i * step;
-                int abs_pos = abs(phase_reduced - pos);
-                kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs_pos] * window_lut[abs_pos];
+                float kernel[SINC_WIDTH * 2], kernel_sum = 0.0f;
+                int phase_reduced = (int)(inv_phase * RESAMPLER_RESOLUTION);
+                int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+                int i = SINC_WIDTH;
+
+                for (; i >= -SINC_WIDTH + 1; --i)
+                {
+                    int pos = i * step;
+                    int window_pos = i * window_step;
+                    kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+                }
+                last_amp += sample;
+                sample /= kernel_sum;
+                for (sample = 0, i = 0; i < SINC_WIDTH * 2; ++i)
+                    out[i] += sample * kernel[i];
             }
-            sample = *in++ - last_amp;
-            last_amp += sample;
-            sample /= kernel_sum;
-            for (sample = 0, i = 0; i < SINC_WIDTH * 2; ++i)
-                out[i] += sample * kernel[i];
             
             inv_phase += inv_phase_inc;
             
-            out += inv_phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            out += (int)inv_phase;
             
-            inv_phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            inv_phase = fmod(inv_phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -480,6 +491,7 @@
     
     return used;
 }
+#endif
 
 #ifdef RESAMPLER_SSE
 static int resampler_run_blep_sse(resampler * r, float ** out_, float * out_end)
@@ -494,50 +506,134 @@
         float const* in = in_;
         float const* const in_end = in + in_size;
         float last_amp = r->last_amp;
-        int inv_phase = r->inv_phase;
-        int inv_phase_inc = r->inv_phase_inc;
+        float inv_phase = r->inv_phase;
+        float inv_phase_inc = r->inv_phase_inc;
         
-        const int step = RESAMPLER_RESOLUTION;
+        const int step = RESAMPLER_BLEP_CUTOFF * RESAMPLER_RESOLUTION;
+        const int window_step = RESAMPLER_RESOLUTION;
         
         do
         {
-            // accumulate in extended precision
-            float kernel_sum = 0.0;
-            __m128 kernel[SINC_WIDTH / 2];
-            __m128 temp1, temp2;
-            __m128 samplex;
             float sample;
-            float *kernelf = (float*)(&kernel);
-            int phase_reduced = inv_phase >> RESAMPLER_SHIFT_EXTRA;
-            int i = SINC_WIDTH;
             
             if ( out + SINC_WIDTH * 2 > out_end )
                 break;
             
-            for (; i >= -SINC_WIDTH + 1; --i)
+            sample = *in++ - last_amp;
+            
+            if (sample)
             {
-                int pos = i * step;
-                int abs_pos = abs(phase_reduced - pos);
-                kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs_pos] * window_lut[abs_pos];
+                float kernel_sum = 0.0f;
+                __m128 kernel[SINC_WIDTH / 2];
+                __m128 temp1, temp2;
+                __m128 samplex;
+                float *kernelf = (float*)(&kernel);
+                int phase_reduced = (int)(inv_phase * RESAMPLER_RESOLUTION);
+                int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+                int i = SINC_WIDTH;
+
+                for (; i >= -SINC_WIDTH + 1; --i)
+                {
+                    int pos = i * step;
+                    int window_pos = i * window_step;
+                    kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+                }
+                last_amp += sample;
+                sample /= kernel_sum;
+                samplex = _mm_set1_ps( sample );
+                for (i = 0; i < SINC_WIDTH / 2; ++i)
+                {
+                    temp1 = _mm_load_ps( (const float *)( kernel + i ) );
+                    temp1 = _mm_mul_ps( temp1, samplex );
+                    temp2 = _mm_loadu_ps( (const float *) out + i * 4 );
+                    temp1 = _mm_add_ps( temp1, temp2 );
+                    _mm_storeu_ps( (float *) out + i * 4, temp1 );
+                }
             }
+            
+            inv_phase += inv_phase_inc;
+            
+            out += (int)inv_phase;
+            
+            inv_phase = fmod(inv_phase, 1.0f);
+        }
+        while ( in < in_end );
+        
+        r->inv_phase = inv_phase;
+        r->last_amp = last_amp;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+#ifdef RESAMPLER_NEON
+static int resampler_run_blep(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 1;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float last_amp = r->last_amp;
+        float inv_phase = r->inv_phase;
+        float inv_phase_inc = r->inv_phase_inc;
+        
+        const int step = RESAMPLER_BLEP_CUTOFF * RESAMPLER_RESOLUTION;
+        const int window_step = RESAMPLER_RESOLUTION;
+        
+        do
+        {
+            float sample;
+            
+            if ( out + SINC_WIDTH * 2 > out_end )
+                break;
+            
             sample = *in++ - last_amp;
-            last_amp += sample;
-            sample /= kernel_sum;
-            samplex = _mm_set1_ps( sample );
-            for (i = 0; i < SINC_WIDTH / 2; ++i)
+            
+            if (sample)
             {
-                temp1 = _mm_load_ps( (const float *)( kernel + i ) );
-                temp1 = _mm_mul_ps( temp1, samplex );
-                temp2 = _mm_loadu_ps( (const float *) out + i * 4 );
-                temp1 = _mm_add_ps( temp1, temp2 );
-                _mm_storeu_ps( (float *) out + i * 4, temp1 );
+                float kernel_sum = 0.0f;
+                float32x4_t kernel[SINC_WIDTH / 2];
+                float32x4_t temp1, temp2;
+                float32x4_t samplex;
+                float *kernelf = (float*)(&kernel);
+                int phase_reduced = (int)(inv_phase * RESAMPLER_RESOLUTION);
+                int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+                int i = SINC_WIDTH;
+
+                for (; i >= -SINC_WIDTH + 1; --i)
+                {
+                    int pos = i * step;
+                    int window_pos = i * window_step;
+                    kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+                }
+                last_amp += sample;
+                sample /= kernel_sum;
+                samplex = vdupq_n_f32(sample);
+                for (i = 0; i < SINC_WIDTH / 2; ++i)
+                {
+                    temp1 = vld1q_f32( (const float32_t *)( kernel + i ) );
+                    temp2 = vld1q_f32( (const float32_t *) out + i * 4 );
+                    temp1 = vmlaq_f32( temp2, temp1, samplex );
+                    vst1q_f32( (float32_t *) out + i * 4, temp1 );
+                }
             }
             
             inv_phase += inv_phase_inc;
             
-            out += inv_phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            out += (int)inv_phase;
             
-            inv_phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            inv_phase = fmod(inv_phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -565,8 +661,8 @@
         float* out = *out_;
         float const* in = in_;
         float const* const in_end = in + in_size;
-        int phase = r->phase;
-        int phase_inc = r->phase_inc;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
         
         do
         {
@@ -575,14 +671,14 @@
             if ( out >= out_end )
                 break;
             
-            sample = in[0] + (in[1] - in[0]) * ((float)phase / RESAMPLER_RESOLUTION_EXTRA);
+            sample = in[0] + (in[1] - in[0]) * phase;
             *out++ = sample;
             
             phase += phase_inc;
             
-            in += phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            in += (int)phase;
             
-            phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            phase = fmod(phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -597,6 +693,287 @@
     return used;
 }
 
+#ifndef RESAMPLER_NEON
+static int resampler_run_blam(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float last_amp = r->last_amp;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
+        float inv_phase = r->inv_phase;
+        float inv_phase_inc = r->inv_phase_inc;
+        
+        const int step = RESAMPLER_BLAM_CUTOFF * RESAMPLER_RESOLUTION;
+        const int window_step = RESAMPLER_RESOLUTION;
+
+        do
+        {
+            float sample;
+            
+            if ( out + SINC_WIDTH * 2 > out_end )
+                break;
+            
+            sample = in[0];
+            if (phase_inc < 1.0f)
+                sample += (in[1] - in[0]) * phase;
+            sample -= last_amp;
+            
+            if (sample)
+            {
+                float kernel[SINC_WIDTH * 2], kernel_sum = 0.0f;
+                int phase_reduced = (int)(inv_phase * RESAMPLER_RESOLUTION);
+                int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+                int i = SINC_WIDTH;
+
+                for (; i >= -SINC_WIDTH + 1; --i)
+                {
+                    int pos = i * step;
+                    int window_pos = i * window_step;
+                    kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+                }
+                last_amp += sample;
+                sample /= kernel_sum;
+                for (sample = 0, i = 0; i < SINC_WIDTH * 2; ++i)
+                    out[i] += sample * kernel[i];
+            }
+            
+            if (inv_phase_inc < 1.0f)
+            {
+                ++in;
+                inv_phase += inv_phase_inc;
+                out += (int)inv_phase;
+                inv_phase = fmod(inv_phase, 1.0f);
+            }
+            else
+            {
+                phase += phase_inc;
+                ++out;
+                in += (int)phase;
+                phase = fmod(phase, 1.0f);
+            }
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        r->inv_phase = inv_phase;
+        r->last_amp = last_amp;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+#ifdef RESAMPLER_SSE
+static int resampler_run_blam_sse(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float last_amp = r->last_amp;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
+        float inv_phase = r->inv_phase;
+        float inv_phase_inc = r->inv_phase_inc;
+        
+        const int step = RESAMPLER_BLAM_CUTOFF * RESAMPLER_RESOLUTION;
+        const int window_step = RESAMPLER_RESOLUTION;
+
+        do
+        {
+            float sample;
+            
+            if ( out + SINC_WIDTH * 2 > out_end )
+                break;
+
+            sample = in[0];
+            if (phase_inc < 1.0f)
+            {
+                sample += (in[1] - in[0]) * phase;
+            }
+            sample -= last_amp;
+            
+            if (sample)
+            {
+                float kernel_sum = 0.0f;
+                __m128 kernel[SINC_WIDTH / 2];
+                __m128 temp1, temp2;
+                __m128 samplex;
+                float *kernelf = (float*)(&kernel);
+                int phase_reduced = (int)(inv_phase * RESAMPLER_RESOLUTION);
+                int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+                int i = SINC_WIDTH;
+
+                for (; i >= -SINC_WIDTH + 1; --i)
+                {
+                    int pos = i * step;
+                    int window_pos = i * window_step;
+                    kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+                }
+                last_amp += sample;
+                sample /= kernel_sum;
+                samplex = _mm_set1_ps( sample );
+                for (i = 0; i < SINC_WIDTH / 2; ++i)
+                {
+                    temp1 = _mm_load_ps( (const float *)( kernel + i ) );
+                    temp1 = _mm_mul_ps( temp1, samplex );
+                    temp2 = _mm_loadu_ps( (const float *) out + i * 4 );
+                    temp1 = _mm_add_ps( temp1, temp2 );
+                    _mm_storeu_ps( (float *) out + i * 4, temp1 );
+                }
+            }
+            
+            if (inv_phase_inc < 1.0f)
+            {
+                ++in;
+                inv_phase += inv_phase_inc;
+                out += (int)inv_phase;
+                inv_phase = fmod(inv_phase, 1.0f);
+            }
+            else
+            {
+                phase += phase_inc;
+                ++out;
+                
+                if (phase >= 1.0f)
+                {
+                    ++in;
+                    phase = fmod(phase, 1.0f);
+                }
+            }
+        }
+        while ( in < in_end );
+
+        r->phase = phase;
+        r->inv_phase = inv_phase;
+        r->last_amp = last_amp;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+#ifdef RESAMPLER_NEON
+static int resampler_run_blam(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float last_amp = r->last_amp;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
+        float inv_phase = r->inv_phase;
+        float inv_phase_inc = r->inv_phase_inc;
+        
+        const int step = RESAMPLER_BLAM_CUTOFF * RESAMPLER_RESOLUTION;
+        const int window_step = RESAMPLER_RESOLUTION;
+
+        do
+        {
+            float sample;
+            
+            if ( out + SINC_WIDTH * 2 > out_end )
+                break;
+            
+            sample = in[0];
+            if (phase_inc < 1.0f)
+                sample += (in[1] - in[0]) * fphase;
+            sample -= last_amp;
+            
+            if (sample)
+            {
+                float kernel_sum = 0.0;
+                float32x4_t kernel[SINC_WIDTH / 2];
+                float32x4_t temp1, temp2;
+                float32x4_t samplex;
+                float *kernelf = (float*)(&kernel);
+                int phase_reduced = (int)(inv_phase * RESAMPLER_RESOLUTION);
+                int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+                int i = SINC_WIDTH;
+
+                for (; i >= -SINC_WIDTH + 1; --i)
+                {
+                    int pos = i * step;
+                    int window_pos = i * window_step;
+                    kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+                }
+                last_amp += sample;
+                sample /= kernel_sum;
+                samplex = vdupq_n_f32(sample);
+                for (i = 0; i < SINC_WIDTH / 2; ++i)
+                {
+                    temp1 = vld1q_f32( (const float32_t *)( kernel + i ) );
+                    temp2 = vld1q_f32( (const float32_t *) out + i * 4 );
+                    temp1 = vmlaq_f32( temp2, temp1, samplex );
+                    vst1q_f32( (float32_t *) out + i * 4, temp1 );
+                }
+            }
+
+            if (inv_phase_inc < 1.0f)
+            {
+                ++in;
+                inv_phase += inv_phase_inc;
+                out += (int)inv_phase;
+                inv_phase = fmod(inv_phase, 1.0f);
+            }
+            else
+            {
+                phase += phase_inc;
+                ++out;
+                
+                if (phase >= 1.0f)
+                {
+                    ++in;
+                    phase = fmod(phase, 1.0f);
+                }
+            }
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        r->inv_phase = inv_phase;
+        r->last_amp = last_amp;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+#ifndef RESAMPLER_NEON
 static int resampler_run_cubic(resampler * r, float ** out_, float * out_end)
 {
     int in_size = r->write_filled;
@@ -608,8 +985,8 @@
         float* out = *out_;
         float const* in = in_;
         float const* const in_end = in + in_size;
-        int phase = r->phase;
-        int phase_inc = r->phase_inc;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
         
         do
         {
@@ -620,7 +997,7 @@
             if ( out >= out_end )
                 break;
             
-            kernel = cubic_lut + (phase >> RESAMPLER_SHIFT_EXTRA) * 4;
+            kernel = cubic_lut + (int)(phase * RESAMPLER_RESOLUTION) * 4;
             
             for (sample = 0, i = 0; i < 4; ++i)
                 sample += in[i] * kernel[i];
@@ -628,9 +1005,9 @@
             
             phase += phase_inc;
             
-            in += phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            in += (int)phase;
             
-            phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            phase = fmod(phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -644,6 +1021,7 @@
     
     return used;
 }
+#endif
 
 #ifdef RESAMPLER_SSE
 static int resampler_run_cubic_sse(resampler * r, float ** out_, float * out_end)
@@ -657,8 +1035,8 @@
         float* out = *out_;
         float const* in = in_;
         float const* const in_end = in + in_size;
-        int phase = r->phase;
-        int phase_inc = r->phase_inc;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
         
         do
         {
@@ -669,7 +1047,7 @@
                 break;
             
             temp1 = _mm_loadu_ps( (const float *)( in ) );
-            temp2 = _mm_load_ps( (const float *)( cubic_lut + (phase >> RESAMPLER_SHIFT_EXTRA) * 4 ) );
+            temp2 = _mm_load_ps( (const float *)( cubic_lut + (int)(phase * RESAMPLER_RESOLUTION) * 4 ) );
             temp1 = _mm_mul_ps( temp1, temp2 );
             samplex = _mm_add_ps( samplex, temp1 );
             temp1 = _mm_movehl_ps( temp1, samplex );
@@ -682,9 +1060,9 @@
             
             phase += phase_inc;
             
-            in += phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            in += (int)phase;
             
-            phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            phase = fmod(phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -700,6 +1078,56 @@
 }
 #endif
 
+#ifdef RESAMPLER_NEON
+static int resampler_run_cubic(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 4;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
+        
+        do
+        {
+            float32x4_t temp1, temp2;
+            float32x2_t half;
+            
+            if ( out >= out_end )
+                break;
+            
+            temp1 = vld1q_f32( (const float32_t *)( in ) );
+            temp2 = vld1q_f32( (const float32_t *)( cubic_lut + (int)(phase * RESAMPLER_RESOLUTION) * 4 ) );
+            temp1 = vmulq_f32( temp1, temp2 );
+            half = vadd_f32(vget_high_f32(temp1), vget_low_f32(temp1));
+            *out++ = vget_lane_f32(vpadd_f32(half, half), 0);
+            
+            phase += phase_inc;
+            
+            in += (int)phase;
+            
+            phase = fmod(phase, 1.0f);
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+#ifndef RESAMPLER_NEON
 static int resampler_run_sinc(resampler * r, float ** out_, float * out_end)
 {
     int in_size = r->write_filled;
@@ -711,10 +1139,10 @@
         float* out = *out_;
         float const* in = in_;
         float const* const in_end = in + in_size;
-        int phase = r->phase;
-        int phase_inc = r->phase_inc;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
 
-        int step = phase_inc > RESAMPLER_RESOLUTION_EXTRA ? RESAMPLER_RESOLUTION * RESAMPLER_RESOLUTION_EXTRA / phase_inc : RESAMPLER_RESOLUTION;
+        int step = phase_inc > 1.0f ? (int)(RESAMPLER_RESOLUTION / phase_inc * RESAMPLER_SINC_CUTOFF) : (int)(RESAMPLER_RESOLUTION * RESAMPLER_SINC_CUTOFF);
         int window_step = RESAMPLER_RESOLUTION;
 
         do
@@ -721,7 +1149,7 @@
         {
             float kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
             int i = SINC_WIDTH;
-            int phase_reduced = phase >> RESAMPLER_SHIFT_EXTRA;
+            int phase_reduced = (int)(phase * RESAMPLER_RESOLUTION);
             int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
             float sample;
 
@@ -740,9 +1168,9 @@
 
             phase += phase_inc;
 
-            in += phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            in += (int)phase;
 
-            phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            phase = fmod(phase, 1.0f);
         }
         while ( in < in_end );
 
@@ -756,6 +1184,7 @@
 
     return used;
 }
+#endif
 
 #ifdef RESAMPLER_SSE
 static int resampler_run_sinc_sse(resampler * r, float ** out_, float * out_end)
@@ -769,10 +1198,10 @@
         float* out = *out_;
         float const* in = in_;
         float const* const in_end = in + in_size;
-        int phase = r->phase;
-        int phase_inc = r->phase_inc;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
         
-        int step = phase_inc > RESAMPLER_RESOLUTION_EXTRA ? RESAMPLER_RESOLUTION * RESAMPLER_RESOLUTION_EXTRA / phase_inc : RESAMPLER_RESOLUTION;
+        int step = phase_inc > 1.0f ? (int)(RESAMPLER_RESOLUTION / phase_inc * RESAMPLER_SINC_CUTOFF) : (int)(RESAMPLER_RESOLUTION * RESAMPLER_SINC_CUTOFF);
         int window_step = RESAMPLER_RESOLUTION;
         
         do
@@ -784,7 +1213,7 @@
             __m128 samplex = _mm_setzero_ps();
             float *kernelf = (float*)(&kernel);
             int i = SINC_WIDTH;
-            int phase_reduced = phase >> RESAMPLER_SHIFT_EXTRA;
+            int phase_reduced = (int)(phase * RESAMPLER_RESOLUTION);
             int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
             
             if ( out >= out_end )
@@ -816,9 +1245,9 @@
             
             phase += phase_inc;
             
-            in += phase >> (RESAMPLER_SHIFT + RESAMPLER_SHIFT_EXTRA);
+            in += (int)phase;
             
-            phase &= RESAMPLER_RESOLUTION_EXTRA - 1;
+            phase = fmod(phase, 1.0f);
         }
         while ( in < in_end );
         
@@ -834,6 +1263,77 @@
 }
 #endif
 
+#ifdef RESAMPLER_NEON
+static int resampler_run_sinc(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= SINC_WIDTH * 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float phase = r->phase;
+        float phase_inc = r->phase_inc;
+        
+        int step = phase_inc > 1.0f ? (int)(RESAMPLER_RESOLUTION / phase_inc * RESAMPLER_SINC_CUTOFF) : (int)(RESAMPLER_RESOLUTION * RESAMPLER_SINC_CUTOFF);
+        int window_step = RESAMPLER_RESOLUTION;
+        
+        do
+        {
+            // accumulate in extended precision
+            float kernel_sum = 0.0;
+            float32x4_t kernel[SINC_WIDTH / 2];
+            float32x4_t temp1, temp2;
+            float32x4_t samplex = {0};
+            float32x2_t half;
+            float *kernelf = (float*)(&kernel);
+            int i = SINC_WIDTH;
+            int phase_reduced = (int)(phase * RESAMPLER_RESOLUTION);
+            int phase_adj = phase_reduced * step / RESAMPLER_RESOLUTION;
+            
+            if ( out >= out_end )
+                break;
+            
+            for (; i >= -SINC_WIDTH + 1; --i)
+            {
+                int pos = i * step;
+                int window_pos = i * window_step;
+                kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase_reduced - window_pos)];
+            }
+            for (i = 0; i < SINC_WIDTH / 2; ++i)
+            {
+                temp1 = vld1q_f32( (const float32_t *)( in + i * 4 ) );
+                temp2 = vld1q_f32( (const float32_t *)( kernel + i ) );
+                samplex = vmlaq_f32( samplex, temp1, temp2 );
+            }
+            kernel_sum = 1.0 / kernel_sum;
+            samplex = vmulq_f32(samplex, vmovq_n_f32(kernel_sum));
+            half = vadd_f32(vget_high_f32(samplex), vget_low_f32(samplex));
+            *out++ = vget_lane_f32(vpadd_f32(half, half), 0);
+            
+            phase += phase_inc;
+            
+            in += (int)phase;
+            
+            phase = fmod(phase, 1.0f);
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
 static void resampler_fill(resampler * r)
 {
     int min_filled = resampler_min_filled(r);
@@ -861,9 +1361,11 @@
             if ( write_extra > SINC_WIDTH * 2 - 1 )
                 write_extra = SINC_WIDTH * 2 - 1;
             memcpy( r->buffer_out + resampler_buffer_size, r->buffer_out, write_extra * sizeof(r->buffer_out[0]) );
+#ifdef RESAMPLER_SSE
             if ( resampler_has_sse )
                 used = resampler_run_blep_sse( r, &out, out + write_size + write_extra );
             else
+#endif
                 used = resampler_run_blep( r, &out, out + write_size + write_extra );
             memcpy( r->buffer_out, r->buffer_out + resampler_buffer_size, write_extra * sizeof(r->buffer_out[0]) );
             if (!used)
@@ -875,6 +1377,27 @@
             resampler_run_linear( r, &out, out + write_size );
             break;
                 
+        case RESAMPLER_QUALITY_BLAM:
+        {
+            float * out_ = out;
+            int write_extra = 0;
+            if ( write_pos >= r->read_pos )
+                write_extra = r->read_pos;
+            if ( write_extra > SINC_WIDTH * 2 - 1 )
+                write_extra = SINC_WIDTH * 2 - 1;
+            memcpy( r->buffer_out + resampler_buffer_size, r->buffer_out, write_extra * sizeof(r->buffer_out[0]) );
+#ifdef RESAMPLER_SSE
+            if ( resampler_has_sse )
+                resampler_run_blam_sse( r, &out, out + write_size + write_extra );
+            else
+#endif
+                resampler_run_blam( r, &out, out + write_size + write_extra );
+            memcpy( r->buffer_out, r->buffer_out + resampler_buffer_size, write_extra * sizeof(r->buffer_out[0]) );
+            if ( out == out_ )
+                return;
+            break;
+        }
+
         case RESAMPLER_QUALITY_CUBIC:
 #ifdef RESAMPLER_SSE
             if ( resampler_has_sse )
@@ -905,7 +1428,7 @@
         int delay = resampler_output_delay( r );
         r->delay_removed = 0;
         while ( delay-- )
-            resampler_remove_sample( r );
+            resampler_remove_sample( r, 1 );
     }
 }
 
@@ -912,7 +1435,7 @@
 int resampler_get_sample_count(void *_r)
 {
     resampler * r = ( resampler * ) _r;
-    if ( r->read_filled < 1 && (r->quality != RESAMPLER_QUALITY_BLEP || r->inv_phase_inc))
+    if ( r->read_filled < 1 && ((r->quality != RESAMPLER_QUALITY_BLEP && r->quality != RESAMPLER_QUALITY_BLAM) || r->inv_phase_inc))
         resampler_fill_and_remove_delay( r );
     return r->read_filled;
 }
@@ -924,7 +1447,7 @@
         resampler_fill_and_remove_delay( r );
     if ( r->read_filled < 1 )
         return 0;
-    if ( r->quality == RESAMPLER_QUALITY_BLEP )
+    if ( r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM )
         return (int)(r->buffer_out[ r->read_pos ] + r->accumulator);
     else
         return (int)r->buffer_out[ r->read_pos ];
@@ -937,24 +1460,27 @@
         resampler_fill_and_remove_delay( r );
     if ( r->read_filled < 1 )
         return 0;
-    if ( r->quality == RESAMPLER_QUALITY_BLEP )
+    if ( r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM )
         return r->buffer_out[ r->read_pos ] + r->accumulator;
     else
         return r->buffer_out[ r->read_pos ];
 }
 
-void resampler_remove_sample(void *_r)
+void resampler_remove_sample(void *_r, int decay)
 {
     resampler * r = ( resampler * ) _r;
     if ( r->read_filled > 0 )
     {
-        if ( r->quality == RESAMPLER_QUALITY_BLEP )
+        if ( r->quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLAM )
         {
             r->accumulator += r->buffer_out[ r->read_pos ];
             r->buffer_out[ r->read_pos ] = 0;
-            r->accumulator -= r->accumulator * (1.0 / 8192.0);
-            if (fabs(r->accumulator) < 1e-20)
-                r->accumulator = 0;
+            if (decay)
+            {
+                r->accumulator -= r->accumulator * (1.0f / 8192.0f);
+                if (fabs(r->accumulator) < 1e-20f)
+                    r->accumulator = 0;
+            }
         }
         --r->read_filled;
         r->read_pos = ( r->read_pos + 1 ) % resampler_buffer_size;