shithub: leaf

Download patch

ref: 3e21094724bf346ebe920419a178cf06720d3a3d
parent: a22f4db65d03820d4fa0f75769758c0c7c5fc76b
author: Matthew Wang <mjw7@princeton.edu>
date: Wed Jul 8 14:22:30 EDT 2020

continuing bacf period detection

--- a/leaf/Inc/leaf-analysis.h
+++ b/leaf/Inc/leaf-analysis.h
@@ -20,6 +20,7 @@
 #include "leaf-math.h"
 #include "leaf-filters.h"
 #include "leaf-envelopes.h"
+#include "leaf-delay.h"
     
     /*!
      * @internal
@@ -675,10 +676,11 @@
     {
         tMempool mempool;
         
-        _tZeroCrossing2* _info;
+        tZeroCrossing2* _info;
+        unsigned int _size;
+        unsigned int _pos;
+        unsigned int _mask;
         
-        int index;
-        
         float                _prev;// = 0.0f;
         float                _hysteresis;
         bool                 _state;// = false;
@@ -708,7 +710,7 @@
     float   tZeroCrossings_getPeak(tZeroCrossings* const zc);
     int     tZeroCrossings_isReset(tZeroCrossings* const zc);
     
-    tZeroCrossing2* const tZeroCrossings_getCrossing(int index);
+    tZeroCrossing2 const tZeroCrossings_getCrossing(tZeroCrossings* const zc, int index);
     
     //==============================================================================
     
@@ -716,6 +718,9 @@
     {
         tMempool mempool;
         
+        unsigned int _value_size;
+        unsigned int _size;
+        unsigned int _bit_size;
         unsigned int* _bits;
     } _tBitset;
     
@@ -725,18 +730,15 @@
     void    tBitset_initToPool  (tBitset* const bitset, int numBits, tMempool* const mempool);
     void    tBitset_free    (tBitset* const bitset);
     
+    int     tBitset_get     (tBitset* const bitset, int index);
+    unsigned int*   tBitset_getData   (tBitset* const bitset);
+    
+    void    tBitset_set     (tBitset* const bitset, int index, unsigned int val);
+    void    tBitset_setMultiple (tBitset* const bitset, int index, int n, unsigned int val);
+
     int     tBitset_getSize (tBitset* const bitset);
-//    bitset&        operator=(bitset const& rhs) = default;
-//    bitset&        operator=(bitset&& rhs) = default;
-//
-//    std::size_t    size() const;
-//    void           clear();
-//    void           set(std::size_t i, bool val);
-//    void           set(std::size_t i, std::size_t n, bool val);
-//    bool           get(std::size_t i) const;
-//
-//    T*             data();
-//    T const*       data() const;
+    void    tBitset_clear   (tBitset* const bitset);
+
     
     //==============================================================================
     
@@ -744,16 +746,74 @@
     {
         tMempool mempool;
         
-        int windowSize;
-        
-        
+        tBitset _bitset;
+        unsigned int _mid_array;
     } _tBACF;
     
     typedef _tBACF* tBACF;
     
-    void    tBACF_init  (tBACF* const, int windowSize);
-    void    tBACF_initToPool    (tBACF* const, int windowSize, tMempool* const);
-    void    tBACF_free  (tBACF* const);
+    void    tBACF_init  (tBACF* const bacf, tBitset* const bitset);
+    void    tBACF_initToPool    (tBACF* const bacf, tBitset* const bitset, tMempool* const mempool);
+    void    tBACF_free  (tBACF* const bacf);
+    
+    int    tBACF_tick  (tBACF* const bacf, int pos);
+    
+    //==============================================================================
+    
+#define PULSE_THRESHOLD 0.6f
+#define HARMONIC_PERIODICITY_FACTOR 16
+#define PERIODICITY_DIFF_FACTOR 0.008f
+    
+    typedef struct _auto_correlation_info
+    {
+        int               _i1;// = -1;
+        int               _i2;// = -1;
+        int               _period;// = -1;
+        float             _periodicity;// = 0.0f;
+        unsigned int      _harmonic;
+    } _auto_correlation_info;
+    
+    typedef struct _sub_collector
+    {
+        tMempool mempool;
+        
+        float             _first_period;
+        
+        _auto_correlation_info _fundamental;
+        
+        // passed in, not initialized
+        tZeroCrossing           _zc;
+        
+        float             _harmonic_threshold;
+        float             _periodicity_diff_threshold;
+        int               _range;
+    } _sub_collector;
+
+    typedef struct _tPeriodDetector
+    {
+        tMempool mempool;
+        
+        tZeroCrossings          _zc;
+        float                   _period;
+        float                   _periodicity;
+        unsigned int            _min_period;
+        int                     _range;
+        tBitset                 _bits;
+        float                   _weight;
+        unsigned int            _mid_point;
+        float                   _periodicity_diff_threshold;
+        float                   _predicted_period;// = -1.0f;
+        unsigned int            _edge_mark;// = 0;
+        unsigned int            _predict_edge;// = 0;
+    } _tPeriodDetector;
+    
+    typedef _tPeriodDetector* tPeriodDetector;
+    
+    void    tPeriodDetector_init    (tPeriodDetector* const detector, float lowestFreq, float highestFreq, float hysteresis);
+    void    tPeriodDetector_initToPool  (tPeriodDetector* const detector, float lowestFreq, float highestFreq, float hysteresis, tMempool* const mempool);
+    void    tPeriodDetector_free    (tPeriodDetector* const detector);
+    
+    float   tPeriodDetector_tick    (tPeriodDetector* const detector, float sample);
     
     
     
--- a/leaf/Inc/leaf-delay.h
+++ b/leaf/Inc/leaf-delay.h
@@ -667,6 +667,29 @@
     
     //==============================================================================
     
+    typedef struct _tRingBuffer
+    {
+        tMempool mempool;
+        
+        float* buffer;
+        unsigned int size;
+        unsigned int pos;
+        unsigned int mask;
+    } _tRingBuffer;
+    
+    typedef _tRingBuffer* tRingBuffer;
+    
+    void    tRingBuffer_init     (tRingBuffer* const ring, int size);
+    void    tRingBuffer_initToPool   (tRingBuffer* const ring, int size, tMempool* const mempool);
+    void    tRingBuffer_free     (tRingBuffer* const ring);
+    
+    void    tRingBuffer_push     (tRingBuffer* const ring, float val);
+    float   tRingBuffer_getNewest    (tRingBuffer* const ring);
+    float   tRingBuffer_getOldest    (tRingBuffer* const ring);
+    float   tRingBuffer_get      (tRingBuffer* const ring, int index);
+    float   tRingBuffer_clear    (tRingBuffer* const ring);
+    int     tRingBuffer_getSize  (tRingBuffer* const ring);
+    
 #ifdef __cplusplus
 }
 #endif
--- a/leaf/Inc/leaf-math.h
+++ b/leaf/Inc/leaf-math.h
@@ -210,6 +210,12 @@
     float maximum (float num1, float num2);
 
     float minimum (float num1, float num2);
+    
+    // built in compiler popcount functions should be faster but we want this to be portable
+    // could try to add some define that call the correct function depending on compiler
+    // or let the user pointer popcount() to whatever they want
+    // something to look into...
+    int popcount(unsigned int x);
 
 
     //==============================================================================
--- a/leaf/Src/leaf-analysis.c
+++ b/leaf/Src/leaf-analysis.c
@@ -1034,9 +1034,9 @@
 }
 
 
-static void update_state(tZeroCrossings* const zc, float s);
-static void shift(tZeroCrossings* const zc, int n);
-static void reset(tZeroCrossings* const zc);
+static inline void update_state(tZeroCrossings* const zc, float s);
+static inline void shift(tZeroCrossings* const zc, int n);
+static inline void reset(tZeroCrossings* const zc);
 
 void    tZeroCrossings_init  (tZeroCrossings* const zc, int windowSize, float hysteresis)
 {
@@ -1053,10 +1053,16 @@
     int bits = CHAR_BIT * sizeof(unsigned int);
     z->_window_size = fmax(2, (windowSize + bits - 1) / bits) * bits;
     
-    // The current ring buffer and indexing implementation for this is less efficient than the original source
-    // May be worth porting over the ring buffer class from https://github.com/cycfi/q/tree/develop
-    z->_info = (_tZeroCrossing2*) mpool_alloc(sizeof(_tZeroCrossing2) * (z->_window_size / 2), m);
-    z->index = -1;
+    int size = z->_window_size / 2;
+    
+    // Ensure size is a power of 2
+    z->_size = pow(2, ceil(log2(size)));
+    z->_mask = z->_size - 1;
+
+    z->_info = (tZeroCrossing2*) mpool_alloc(sizeof(tZeroCrossing2) * z->_size, m);
+    for (int i = 0; i < z->_size; i++)
+        tZeroCrossing2_initToPool(&z->_info[i], mp);
+    z->_pos = 0;
 }
 
 void    tZeroCrossings_free  (tZeroCrossings* const zc)
@@ -1070,6 +1076,30 @@
 {
     _tZeroCrossings* z = *zc;
     
+    // Offset s by half of hysteresis, so that zero cross detection is
+    // centered on the actual zero.
+    s += z->_hysteresis * 0.5f;
+    
+    if (z->_num_edges >= z->_size)
+        reset(zc);
+    
+    if ((z->_frame == z->_window_size/2) && z->_num_edges == 0)
+        reset(zc);
+    
+    update_state(zc, s);
+    
+    if (++z->_frame >= z->_window_size && !z->_state)
+    {
+        // Remove half the size from _frame, so we can continue seamlessly
+        z->_frame -= z->_window_size / 2;
+        
+        // We need at least two rising edges.
+        if (z->_num_edges > 1)
+            z->_ready = true;
+        else
+            reset(zc);
+    }
+    
     return z->_state;
 }
 
@@ -1080,6 +1110,13 @@
     return z->_state;
 }
 
+tZeroCrossing2 const tZeroCrossings_getCrossing(tZeroCrossings* const zc, int index)
+{
+    _tZeroCrossings* z = *zc;
+    
+    return z->_info[(z->_num_edges - 1) - index];
+}
+
 int     tZeroCrossings_getNumEdges(tZeroCrossings* const zc)
 {
     _tZeroCrossings* z = *zc;
@@ -1091,7 +1128,7 @@
 {
     _tZeroCrossings* z = *zc;
     
-    return z->_window_size / 2;
+    return z->_size;
 }
 
 int     tZeroCrossings_getFrame(tZeroCrossings* const zc)
@@ -1129,7 +1166,7 @@
     return z->_frame == 0;
 }
 
-static void update_state(tZeroCrossings* const zc, float s)
+static inline void update_state(tZeroCrossings* const zc, float s)
 {
     _tZeroCrossings* z = *zc;
     
@@ -1149,8 +1186,9 @@
     {
         if (!z->_state)
         {
-            ++z->index;
-            _tZeroCrossing2 crossing = z->_info[z->index];
+            --z->_pos;
+            z->_pos &= z->_mask;
+            _tZeroCrossing2 crossing = *z->_info[z->_pos];
             crossing._before_crossing = z->_prev;
             crossing._after_crossing = s;
             crossing._peak = s;
@@ -1160,8 +1198,7 @@
         }
         else
         {
-            tZeroCrossing2 ptr = &z->_info[z->index];
-            tZeroCrossing2_updatePeak(&ptr, s, z->_frame);
+            tZeroCrossing2_updatePeak(&z->_info[z->_pos], s, z->_frame);
         }
         if (s > z->_peak_update)
         {
@@ -1171,7 +1208,7 @@
     else if (z->_state && (s < z->_hysteresis))
     {
         z->_state = 0;
-        z->_info[z->index]._trailing_edge = z->_frame;
+        z->_info[z->_pos]->_trailing_edge = z->_frame;
         if (z->_peak == 0.0f)
             z->_peak = z->_peak_update;
     }
@@ -1179,21 +1216,21 @@
     z->_prev = s;
 }
 
-static void shift(tZeroCrossings* const zc, int n)
+static inline void shift(tZeroCrossings* const zc, int n)
 {
     _tZeroCrossings* z = *zc;
     
-    _tZeroCrossing2 crossing = z->_info[z->index];
+    tZeroCrossing2 crossing = z->_info[z->_pos];
     
-    crossing._leading_edge -= n;
+    crossing->_leading_edge -= n;
     if (!z->_state)
-        crossing._trailing_edge -= n;
+        crossing->_trailing_edge -= n;
     int i = 1;
     for (; i != z->_num_edges; ++i)
     {
-        int idx = (z->index + i) % (z->_window_size / 2);
-        z->_info[idx]._leading_edge -= n;
-        int edge = (z->_info[idx]._trailing_edge -= n);
+        int idx = (z->_pos + i) & z->_mask;
+        z->_info[idx]->_leading_edge -= n;
+        int edge = (z->_info[idx]->_trailing_edge -= n);
         if (edge < 0.0f)
             break;
     }
@@ -1200,7 +1237,7 @@
     z->_num_edges = i;
 }
 
-static void reset(tZeroCrossings* const zc)
+static inline void reset(tZeroCrossings* const zc)
 {
     _tZeroCrossings* z = *zc;
     
@@ -1207,4 +1244,310 @@
     z->_num_edges = 0;
     z->_state = 0;
     z->_frame = 0;
+}
+
+
+
+void    tBitset_init    (tBitset* const bitset, int numBits)
+{
+    tBitset_initToPool(bitset, numBits, &leaf.mempool);
+}
+
+void    tBitset_initToPool  (tBitset* const bitset, int numBits, tMempool* const mempool)
+{
+    _tMempool* m = *mempool;
+    _tBitset* b = *bitset = (_tBitset*) mpool_alloc(sizeof(_tBitset), m);
+    b->mempool = m;
+    
+    // Size of the array value in bits
+    b->_value_size = (CHAR_BIT * sizeof(unsigned int));
+    
+    // Size of the array needed to store numBits bits
+    b->_size = (numBits + b->_value_size - 1) / b->_value_size;
+    
+    // Siz of the array in bits
+    b->_bit_size = b->_size * b->_value_size;
+    
+    b->_bits = (unsigned int*) mpool_calloc(sizeof(unsigned int) * b->_size, m);
+}
+
+void    tBitset_free    (tBitset* const bitset)
+{
+    _tBitset* b = *bitset;
+    
+    mpool_free((char*) b->_bits, b->mempool);
+    mpool_free((char*) b, b->mempool);
+}
+
+int     tBitset_get     (tBitset* const bitset, int index)
+{
+    _tBitset* b = *bitset;
+    
+    // Check we don't get past the storage
+    if (index > b->_bit_size)
+        return -1;
+    
+    unsigned int mask = 1 << (index % b->_value_size);
+    return (b->_bits[index / b->_value_size] & mask) != 0;
+}
+
+unsigned int*   tBitset_getData   (tBitset* const bitset)
+{
+    _tBitset* b = *bitset;
+    
+    return b->_bits;
+}
+
+void     tBitset_set     (tBitset* const bitset, int index, unsigned int val)
+{
+    _tBitset* b = *bitset;
+    
+    if (index > b->_bit_size)
+        return;
+    
+    unsigned int mask = 1 << (index % b->_value_size);
+    int i = index / b->_value_size;
+    b->_bits[i] ^= (-val ^ b->_bits[i]) & mask;
+}
+
+void     tBitset_setMultiple (tBitset* const bitset, int index, int n, unsigned int val)
+{
+    _tBitset* b = *bitset;
+    
+    // Check that the index (i) does not get past size
+    if (index > b->_bit_size)
+        return;
+    
+    // Check that the n does not get past the size
+    if ((index+n) > b->_bit_size)
+        n = b->_bit_size - index;
+    
+    // index is the bit index, i is the integer index
+    int i = index / b->_value_size;
+    
+    // Do the first partial int
+    int mod = index & (b->_value_size - 1);
+    if (mod)
+    {
+        // mask off the high n bits we want to set
+        mod = b->_value_size - mod;
+        
+        // Calculate the mask
+        unsigned int mask = ~(UINT_MAX >> mod);
+        
+        // Adjust the mask if we're not going to reach the end of this int
+        if (n < mod)
+            mask &= (UINT_MAX >> (mod - n));
+        
+        if (val)
+            b->_bits[i] |= mask;
+        else
+            b->_bits[i] &= ~mask;
+        
+        // Fast exit if we're done here!
+        if (n < mod)
+            return;
+        
+        n -= mod;
+        ++i;
+    }
+    
+    // Write full ints while we can - effectively doing value_size bits at a time
+    if (n >= b->_value_size)
+    {
+        // Store a local value to work with
+        unsigned int val_ = val ? UINT_MAX : 0;
+        
+        do
+        {
+            b->_bits[i++] = val_;
+            n -= b->_value_size;
+        }
+        while (n >= b->_value_size);
+    }
+    
+    // Now do the final partial int, if necessary
+    if (n)
+    {
+        mod = n & (b->_value_size - 1);
+        
+        // Calculate the mask
+        unsigned int mask = (1 << mod) - 1;
+        
+        if (val)
+            b->_bits[i] |= mask;
+        else
+            b->_bits[i] &= ~mask;
+    }
+}
+
+int     tBitset_getSize (tBitset* const bitset)
+{
+    _tBitset* b = *bitset;
+    
+    return b->_bit_size;
+}
+
+void    tBitset_clear   (tBitset* const bitset)
+{
+    _tBitset* b = *bitset;
+    
+    for (int i = 0; i < b->_size; ++i)
+    {
+        b->_bits[i] = 0;
+    }
+}
+
+
+
+void    tBACF_init  (tBACF* const bacf, tBitset* const bitset)
+{
+    tBACF_initToPool(bacf, bitset, &leaf.mempool);
+}
+
+void    tBACF_initToPool    (tBACF* const bacf, tBitset* const bitset, tMempool* const mempool)
+{
+    _tMempool* m = *mempool;
+    _tBACF* b = *bacf = (_tBACF*) mpool_alloc(sizeof(_tBACF), m);
+    b->mempool = m;
+    
+    b->_bitset = *bitset;
+    b->_mid_array = ((b->_bitset->_bit_size / b->_bitset->_value_size) / 2) - 1;
+}
+
+void    tBACF_free  (tBACF* const bacf)
+{
+    _tBACF* b = *bacf;
+    
+    mpool_free((char*) b, b->mempool);
+}
+
+int    tBACF_tick  (tBACF* const bacf, int pos)
+{
+    _tBACF* b = *bacf;
+    
+    int value_size = b->_bitset->_value_size;
+    const int index = pos / value_size;
+    const int shift = pos % value_size;
+    
+    const unsigned int* p1 = b->_bitset->_bits;
+    const unsigned int* p2 = b->_bitset->_bits + index;
+    int count = 0;
+    
+    if (shift == 0)
+    {
+        for (int i = 0; i != b->_mid_array; ++i)
+            // built in compiler popcount functions should be faster but we want this to be portable
+            // could try to add some define that call the correct function depending on compiler
+            // or let the user pointer popcount() to whatever they want
+            // something to look into...
+            count += popcount(*p1++ ^ *p2++);
+    }
+    else
+    {
+        const int shift2 = value_size - shift;
+        for (int i = 0; i != b->_mid_array; ++i)
+        {
+            unsigned int v = *p2++ >> shift;
+            v |= *p2 << shift2;
+            count += popcount(*p1++ ^ v);
+        }
+    }
+    return count;
+    
+}
+
+static inline void set_bitstream(tPeriodDetector* const detector);
+static inline void autocorrelate(tPeriodDetector* const detector);
+static inline void sub_collector_init(_sub_collector collector, tZeroCrossings* const crossings, float pdt, int range);
+
+void    tPeriodDetector_init    (tPeriodDetector* const detector, float lowestFreq, float highestFreq, float hysteresis)
+{
+    tPeriodDetector_initToPool(detector, lowestFreq, highestFreq, hysteresis, &leaf.mempool);
+}
+
+void    tPeriodDetector_initToPool  (tPeriodDetector* const detector, float lowestFreq, float highestFreq, float hysteresis, tMempool* const mempool)
+{
+    _tMempool* m = *mempool;
+    _tPeriodDetector* p = *detector = (_tPeriodDetector*) mpool_alloc(sizeof(_tPeriodDetector), m);
+    p->mempool = m;
+    
+    tZeroCrossings_initToPool(&p->_zc, (1.0f / lowestFreq) * leaf.sampleRate * 2.0f, hysteresis, mempool);
+    p->_min_period = (1.0f / highestFreq) * leaf.sampleRate;
+    p->_range = highestFreq / lowestFreq;
+    
+    int windowSize = tZeroCrossings_getWindowSize(&p->_zc);
+    tBitset_initToPool(&p->_bits, windowSize, mempool);
+    p->_weight = 2.0f / windowSize;
+    p->_mid_point = windowSize / 2;
+    p->_periodicity_diff_threshold = p->_mid_point * PERIODICITY_DIFF_FACTOR;
+    
+    p->_predicted_period = -1.0f;
+    p->_edge_mark = 0;
+    p->_predict_edge = 0;
+}
+
+void    tPeriodDetector_free    (tPeriodDetector* const detector)
+{
+    _tPeriodDetector* p = *detector;
+    
+    tZeroCrossings_free(&p->_zc);
+    tBitset_free(&p->_bits);
+    
+    mpool_free((char*) p, p->mempool);
+}
+
+float   tPeriodDetector_tick    (tPeriodDetector* const detector, float s)
+{
+    _tPeriodDetector* p = *detector;
+    
+    // Zero crossing
+    int prev = tZeroCrossings_getState(&p->_zc);
+    int zc = tZeroCrossings_tick(&p->_zc, s);
+    
+    if (!zc && prev != zc)
+    {
+        ++p->_edge_mark;
+        p->_predicted_period = -1.0f;
+    }
+    
+    if (tZeroCrossings_isReset(&p->_zc))
+    {
+        p->_period = -1;
+        p->_periodicity = 0.0f;
+    }
+    
+    if (tZeroCrossings_isReady(&p->_zc))
+    {
+        set_bitstream(detector);
+        autocorrelate(detector);
+        return true;
+    }
+    return false;
+}
+
+
+static inline void set_bitstream(tPeriodDetector* const detector)
+{
+    _tPeriodDetector* p = *detector;
+    
+    float threshold = tZeroCrossings_getPeak(&p->_zc) * PULSE_THRESHOLD;
+    
+    tBitset_clear(&p->_bits);
+
+    for (int i = 0; i != tZeroCrossings_getNumEdges(&p->_zc); ++i)
+    {
+        tZeroCrossing2 info = tZeroCrossings_getCrossing(&p->_zc, i);
+        if (info->_peak >= threshold)
+        {
+            int pos = fmax(info->_leading_edge, 0);
+            int n = info->_trailing_edge - pos;
+            tBitset_setMultiple(&p->_bits, pos, n, 1);
+        }
+    }
+}
+
+static inline void autocorrelate(tPeriodDetector* const detector)
+{
+    _tPeriodDetector* p = *detector;
 }
--- a/leaf/Src/leaf-delay.c
+++ b/leaf/Src/leaf-delay.c
@@ -901,3 +901,69 @@
     return d->gain;
 }
 
+
+
+void    tRingBuffer_init     (tRingBuffer* const ring, int size)
+{
+    tRingBuffer_initToPool(ring, size, &leaf.mempool);
+}
+
+void    tRingBuffer_initToPool   (tRingBuffer* const ring, int size, tMempool* const mempool)
+{
+    _tMempool* m = *mempool;
+    _tRingBuffer* r = *ring = (_tRingBuffer*) mpool_alloc(sizeof(_tRingBuffer), m);
+    r->mempool = m;
+    
+    // Ensure size is a power of 2
+    if (size <= 0) r->size = 1;
+    else r->size = pow(2, ceil(log2(size)));
+    r->mask = r->size - 1;
+    
+    r->buffer = (float*) mpool_calloc(sizeof(float) * r->size, m);
+    r->pos = 0;
+}
+
+void    tRingBuffer_free     (tRingBuffer* const ring)
+{
+    _tRingBuffer* r = *ring;
+    
+    mpool_free((char*) r->buffer, r->mempool);
+    mpool_free((char*) r, r->mempool);
+}
+
+void   tRingBuffer_push     (tRingBuffer* const ring, float val)
+{
+    _tRingBuffer* r = *ring;
+    
+    --r->pos;
+    r->pos &= r->mask;
+    r->buffer[r->pos] = val;
+}
+
+float   tRingBuffer_getNewest    (tRingBuffer* const ring)
+{
+    _tRingBuffer* r = *ring;
+    
+    return r->buffer[r->pos];
+}
+
+float   tRingBuffer_getOldest    (tRingBuffer* const ring)
+{
+    _tRingBuffer* r = *ring;
+    
+    return r->buffer[(r->pos + r->size - 1) & r->mask];
+}
+
+float   tRingBuffer_get      (tRingBuffer* const ring, int index)
+{
+    _tRingBuffer* r = *ring;
+    
+    return r->buffer[(r->pos + index) & r->mask];
+}
+
+int     tRingBuffer_getSize  (tRingBuffer* const ring)
+{
+    _tRingBuffer* r = *ring;
+    
+    return r->size;
+}
--- a/leaf/Src/leaf-math.c
+++ b/leaf/Src/leaf-math.c
@@ -720,4 +720,14 @@
     return (num1 < num2 ) ? num1 : num2;
 }
 
-
+// built in compiler popcount functions should be faster but we want this to be portable
+// could try to add some define that call the correct function depending on compiler
+// or let the user pointer popcount() to whatever they want
+// something to look into...
+int popcount(unsigned int x)
+{
+    int c = 0;
+    for (; x != 0; x &= x - 1)
+        c++;
+    return c;
+}