keldonalleyne

Efficient streaming sliding window algorithm

Jun 29th, 2016
473
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.74 KB | None | 0 0
  1. /*** peakhold.h
  2. *******************************************************************************/
  3. /*
  4.  * Streaming sliding window algorithm with O(M log N). M = number of samples,
  5.  * N is 2^n windowsize). This algorithm is pretty fast as it relies only on
  6.  * simple data structures and is further strategically optimised by choice
  7.  * of buffer sizes.
  8.  *
  9.  * Pushing a new sample is O(log N) in the worst case, but is around O(1)
  10.  * on average (N being window size). There are reasons for this I may explain
  11.  * at a later time.
  12.  *
  13.  * Finding the maximum value in a window is O(log N). To give you an idea of
  14.  * its speed, on a laptop i5 compiled with GCC -O3, it can calculate 100
  15.  *  seconds of samples at 44.1kz with a window size of 1 second in just 2000ms.
  16.  *
  17.  * More info: http://algorithmsandme.in/2014/03/heaps-sliding-window-algorithm/
  18.  *
  19.  * TODO: Rename peakhold to sliding_window (was originally written for
  20.  * audio DSP).
  21.  */
  22.  
  23. #ifndef PEAK_HOLD_H
  24. #define PEAK_HOLD_H
  25.  
  26. #include <stdlib.h> /* size_t */
  27.  
  28. #ifdef __cplusplus
  29. extern "C" {
  30. #endif
  31.  
  32.  
  33. /*! \brief Allocates instance with zeroed buffers. */
  34. struct PeakHold* PeakHold_Create(size_t max_window_size);
  35. /*! \brief Destroys all memory associated with the instance */
  36. void PeakHold_Destroy(struct PeakHold *self);
  37. /*! \brief Clears all buffers */
  38. void PeakHold_Clear(struct PeakHold *self);
  39. /*! \brief Inserts next sample */
  40. void PeakHold_Insert(struct PeakHold *self, float value);
  41. /*! \brief Returns maximum sample within the given window size */
  42. float PeakHold_CalcMaxPeak(struct PeakHold *self, size_t window_size);
  43.  
  44. #ifdef __cplusplus
  45. }
  46. #endif
  47.  
  48. #endif /* PEAK_HOLD_H */
  49.  
  50.  
  51. /*** peakhold.c
  52. *******************************************************************************/
  53. #include "peakhold.h"
  54.  
  55. #include <stdio.h>
  56. #include <math.h>
  57. #include <float.h>
  58. #include <time.h>
  59. #include <stdlib.h>
  60. #include <stddef.h>
  61. #include <assert.h>
  62. #include <stdbool.h>
  63. #include <string.h>
  64.  
  65. struct PeakHold_Sample {
  66.   float value;
  67.   unsigned int is_valid;
  68. };
  69.  
  70. typedef unsigned char PeakHold_Bool;
  71. enum { PeakHold_False, PeakHold_True = !PeakHold_False };
  72.  
  73. typedef struct PeakHold_Sample *PeakHold_SamplePtr;
  74.  
  75. struct PeakHold {
  76.   /*! \brief Stores layers of ring buffers.
  77.  
  78.      Holds a binary tree of samples where a parent
  79.      holds the max value of its two children. level_0
  80.      holds the leaf nodes. See below, level_1[3] is equal
  81.      to Max(level_0[6], level_0[7]). level_0[6] and
  82.      level_0[7] are the children of level_1[3].
  83.  
  84.      The buffer is 'buffer_size' entries long, with
  85.      level_0 being buffer_size/2 entries long.
  86.  
  87.      Each level is half the size of the previous. level_0
  88.      is first in memory, followed by level_1, and so on.
  89.      
  90.      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | level_0
  91.      |   3   |   2   |   1   |   0   | level_1
  92.      |       1       |       0       | level_2
  93.   */
  94.   struct PeakHold_Sample *buffer;
  95.   /*! Stores pointers into 'buffer' for each level. */
  96.   PeakHold_SamplePtr *level_ptr;
  97.   /*! Number of entries in each level. */
  98.   size_t *level_size;
  99.   size_t levels;
  100.   size_t buffer_size;
  101.   size_t position;
  102. };
  103.  
  104. static long long PeakHold_RandU64() {
  105.   long long value = 0;
  106.   for(int i = 0; i < 8; ++i) {
  107.     ((char*)&value)[i] = rand();
  108.   }
  109.   return value;
  110. }
  111.  
  112. static float PeakHold_RandFloat() {
  113.   float fval = rand() & 0x7fff;
  114.   fval *= (rand() & 1) ? 1.f : -1.f;
  115.   fval /= 32768.f;
  116.   fval *= 100.f;
  117.   return fval;
  118. }
  119.  
  120. void PeakHold_Destroy(struct PeakHold *self) {
  121.   free(self->level_size);
  122.   self->level_size = 0;
  123.   free(self->level_ptr);
  124.   self->level_ptr = 0;
  125.   free(self->buffer);
  126.   self->buffer = 0;
  127.   self->buffer_size = 0;
  128.   free(self);
  129. }
  130.  
  131. static void PeakHold_SetSample(struct PeakHold *self, size_t level, size_t idx,
  132.             float value);
  133.  
  134. static float PeakHold_FloatMax(float a, float b) { return a > b ? a : b; }
  135.  
  136. static int SizetIsPow2(size_t val) { return val == (val & -val); }
  137. static int UintIsPow2(unsigned int val) { return val == (val & -val); }
  138. static int IntIsPow2(int val) { return val == (val & -val); }
  139.  
  140. static size_t PeakHold_MinSizeT(size_t a, size_t b) { return a < b ? a : b; }
  141. static size_t PeakHold_MaxSizeT(size_t a, size_t b) { return a > b ? a : b; }
  142. static size_t PeakHold_CalcLevels(size_t size);
  143.  
  144. struct PeakHold* PeakHold_Create(size_t max_window_size) {
  145.  
  146.   /* Find power of 2 size that is at least twice the size of `max_window_size` */
  147.  
  148.   size_t actual_size = 1;
  149.   while(actual_size < (max_window_size * 2)) actual_size <<= 1;
  150.  
  151.  
  152.  
  153.   struct PeakHold *ptr = calloc(1, sizeof(struct PeakHold));
  154.   ptr->buffer = calloc(actual_size, sizeof(struct PeakHold_Sample));
  155.   ptr->buffer_size = actual_size;
  156.   ptr->position = 0;
  157.  
  158.  
  159.   /* Calculate number of levels */
  160.   ptr->levels = PeakHold_CalcLevels(ptr->buffer_size / 2);
  161.   ptr->level_ptr = calloc(ptr->levels, sizeof(PeakHold_SamplePtr));
  162.   ptr->level_size = calloc(ptr->levels, sizeof(size_t));
  163.  
  164.   ptr->level_ptr[0] = ptr->buffer;
  165.   ptr->level_size[0] = ptr->buffer_size / 2;
  166.  
  167.   for(size_t i = 1; i < ptr->levels; ++i) {
  168.     ptr->level_ptr[i] = ptr->level_ptr[i-1] + ptr->level_size[i-1];
  169.     ptr->level_size[i] = ptr->level_size[i-1] / 2;
  170.   }
  171.  
  172.   return ptr;
  173. }
  174.  
  175. void PeakHold_Clear(struct PeakHold *self) {
  176.   memset(self->buffer, 0, sizeof(struct PeakHold_Sample) * self->buffer_size);
  177.   self->position = 0;
  178. }
  179.  
  180. void PeakHold_Insert(struct PeakHold *self, float value) {
  181.   PeakHold_SetSample(self, 0, self->position, value);
  182.   ++self->position;
  183.   self->position &= self->buffer_size / 2 - 1;
  184. }
  185.  
  186. static PeakHold_Bool PeakHold_IsSampleValid(struct PeakHold *self, size_t level,
  187.                      size_t idx) {
  188.   assert(self);
  189.   assert(level < self->levels);
  190.   /* buffer_size is a power of 2, thus this will mask / wrap idx */
  191.   idx &= self->buffer_size / 2 - 1;
  192.   assert(idx < self->buffer_size);
  193.  
  194.   size_t level_idx = idx >> level;
  195.   return self->level_ptr[level][level_idx].is_valid;
  196. }
  197.  
  198. static float PeakHold_GetSampleAt(struct PeakHold *self, size_t level, size_t idx) {
  199.   assert(self);
  200.   assert(level < self->levels);
  201.   /* buffer_size is a power of 2, thus this will mask / wrap idx */
  202.   idx &= self->buffer_size - 1;
  203.   assert(idx < self->buffer_size);
  204.  
  205.   size_t level_idx = idx >> level;
  206.   return self->level_ptr[level][level_idx].value;
  207. }
  208.  
  209. /* Returns highest level with valid samples at the given sample position */
  210. static size_t PeakHold_GetHighestLevel(struct PeakHold *self, size_t max_length,
  211.                 size_t idx) {
  212.   size_t highest_level = 0;
  213.   for(size_t level = 0; level < self->levels; ++level) {
  214.     size_t size = 1 << level;
  215.     PeakHold_Bool is_valid = PeakHold_IsSampleValid(self, level, idx);
  216.     PeakHold_Bool can_fit = size <= max_length;
  217.     if(!(is_valid && can_fit))
  218.       return highest_level;
  219.     highest_level = level;
  220.   }
  221.  
  222.   return highest_level;
  223. }
  224.  
  225. float PeakHold_CalcMaxPeak(struct PeakHold *self, size_t window_size) {
  226.   assert(self);
  227.   if(window_size == 0)
  228.     return 0.f;
  229.  
  230.   float max_peak = -FLT_MAX;
  231.  
  232.   size_t idx_mask = self->buffer_size / 2 - 1;
  233.  
  234.   size_t offset = 0;
  235.   size_t level = 0;
  236.   while(offset < window_size) {
  237.     size_t idx = (self->position - offset - 1) & idx_mask;
  238.     size_t remaining_size = window_size - offset;
  239.     size_t level = PeakHold_GetHighestLevel(self, remaining_size, idx);
  240.     size_t level_size = 1 << level;
  241.     size_t is_valid = PeakHold_IsSampleValid(self, level, idx);
  242.  
  243.  
  244.     /* Window may include samples not yet set. */
  245.     if(!is_valid)
  246.       return max_peak;
  247.  
  248.     float cur_value = PeakHold_GetSampleAt(self, level, idx);
  249.     max_peak = PeakHold_FloatMax(max_peak, cur_value);
  250.    
  251.     offset += level_size;
  252.   }
  253.  
  254.   return max_peak;
  255. }
  256.  
  257. /* Calculates the number of buffer levels for a given power-of-2 window size */
  258. static size_t PeakHold_CalcLevels(size_t size){
  259.   size_t l = size;
  260.   size_t levels = 0;
  261.   while(l > 1) {
  262.     l >>= 1;
  263.     ++levels;
  264.   }
  265.   return levels;
  266. }
  267.  
  268. static void PrintAll(struct PeakHold_Sample *sample, size_t sz) {
  269.   size_t level = 0;
  270.   while(sz > 1) {
  271.     printf("Level %zu (%zu) :: %p ***\n", level, sz, sample);
  272.     for(size_t i = 0; i < sz; ++i) {
  273.       printf("  sample(%f), is_valid(%s)\n", sample[i].value,
  274.          sample[i].is_valid ? "true" : "false");
  275.     }
  276.     sample += sz;
  277.     sz /= 2;
  278.     ++level;
  279.   }
  280. }
  281.  
  282. static void PeakHold_InvalidateSample(struct PeakHold *self, size_t level,
  283.                    size_t idx) {
  284.   PeakHold_Bool has_child = level < self->levels - 1;
  285.   PeakHold_Bool invalidate_child = idx % 2 == 0;
  286.   self->level_ptr[level][idx].is_valid = PeakHold_False;
  287.   if(has_child && invalidate_child)
  288.     PeakHold_InvalidateSample(self, level + 1, idx / 2);
  289. }
  290.  
  291. /* Sets the sample at a given sample index */
  292. static void PeakHold_SetSample(struct PeakHold *self, size_t level, size_t idx,
  293.             float value) {
  294.   struct PeakHold_Sample *cur_level = self->level_ptr[level];
  295.   self->level_ptr[level][idx].value = value;
  296.   self->level_ptr[level][idx].is_valid = PeakHold_True;
  297.   PeakHold_Bool validate_child = idx % 2 == 1;
  298.   PeakHold_Bool has_child = level < self->levels - 1;
  299.   if(!has_child)
  300.     return;
  301.  
  302.  
  303.   if(validate_child){
  304.     float max_val = PeakHold_FloatMax(cur_level[idx].value,
  305.                       cur_level[idx ^ 1].value);
  306.     PeakHold_SetSample(self, level+1, idx / 2, max_val);
  307.   } else {
  308.     PeakHold_InvalidateSample(self, level+1, idx / 2);
  309.   }
  310. }
  311.  
  312. /* Tests the method for calculating number of levels based on max_window_size */
  313. static void TestCalcLevels() {
  314.   assert(PeakHold_CalcLevels(0L) == 0L);
  315.   assert(PeakHold_CalcLevels(1L) == 0L);
  316.   assert(PeakHold_CalcLevels(2L) == 1L);
  317.   assert(PeakHold_CalcLevels(4L) == 2L);
  318.   assert(PeakHold_CalcLevels(8L) == 3L);
  319.   assert(PeakHold_CalcLevels(16L) == 4L);
  320. }
  321.  
  322. /* Tests power of 2 test */
  323. static void TestPow2() {
  324.   assert(SizetIsPow2(2));
  325.   assert(SizetIsPow2(4UL));
  326.   assert(!SizetIsPow2(3UL));
  327.   assert(IntIsPow2(4));
  328.   assert(UintIsPow2(8));
  329.   assert(!UintIsPow2(3));
  330. }
  331.  
  332. static void Print(struct PeakHold *ptr) {
  333.   PrintAll(ptr->buffer, ptr->buffer_size/2);
  334. }
  335.  
  336. static void PeakHold_BruteSearch(const float *arr, float *out, size_t sz,
  337.               size_t window_sz) {
  338.   for(size_t i = 0; i < sz; ++i) {
  339.     float max_val = -FLT_MAX;
  340.     for(size_t j = 0; j < window_sz && j <= i; ++j) {
  341.       max_val = PeakHold_FloatMax(max_val, arr[i - j]);
  342.     }
  343.     out[i] = max_val;
  344.   }
  345. }
  346.  
  347. static void PrintSamples(struct PeakHold *self) {
  348.   for(size_t i = 0; i < self->buffer_size; ++i) {
  349.     PeakHold_Bool is_valid = PeakHold_IsSampleValid(self, 0, i);
  350.     if(i%4 == 0 && i > 0)
  351.       printf("\n");
  352.     if(is_valid) {
  353.       printf("%f,\t", PeakHold_GetSampleAt(self, 0, i));
  354.     } else {
  355.       printf("xxxxxxxx,\t");
  356.     }
  357.   }
  358.  
  359.   printf("\n");
  360. }
  361.  
  362. static void TimePeakHold(int seconds, size_t window_size) {
  363.   size_t sample_count = (size_t)seconds * 44100ULL;
  364.   struct PeakHold *ptr = PeakHold_Create(window_size);
  365.   time_t timer = time(0);
  366.   for(size_t sample_pos = 0; sample_pos < sample_count; ++sample_pos) {
  367.     PeakHold_Insert(ptr, PeakHold_RandFloat());
  368.     (void)PeakHold_CalcMaxPeak(ptr, window_size);
  369.   }
  370.   time_t end = time(0);
  371.  
  372.   PeakHold_Destroy(ptr);
  373.  
  374.   int diff = difftime(end, timer);
  375.  
  376.   printf("Seconds (%d), Window (%zu): seconds (%ds)\n", seconds,
  377.      window_size, diff);
  378. }
  379.  
  380. static float* CreateFloatData(size_t size) {
  381.   float *data = malloc(size * sizeof(float));
  382.   for(size_t i = 0; i < size; ++i)
  383.     data[i] = PeakHold_RandFloat();
  384.  
  385.   return data;
  386. }
  387.  
  388. static void PeakHold_HoldSearch(struct PeakHold *self, const float *data, float *out,
  389.              size_t sz, size_t window_size) {
  390.   for(size_t i = 0; i < sz; ++i) {
  391.     PeakHold_Insert(self, data[i]);
  392.     out[i] = PeakHold_CalcMaxPeak(self, window_size);
  393.   }
  394. }
  395.  
  396. static PeakHold_Bool FloatNearEq(float a, float b) {
  397.   if(a == 0) return b == 0;
  398.  
  399.   float norm = b / a;
  400.   float diff = fabs(1.f - norm);
  401.   return diff < 0.000001f;
  402. }  
  403.  
  404. static void ValidatePeakHold(float seconds, size_t window_size) {
  405.   size_t sample_count = (size_t)(seconds * 44100.f);
  406.   float *data = CreateFloatData(sample_count);
  407.   float *brute_calc = calloc(sample_count, sizeof(float));
  408.   float *hold_calc = calloc(sample_count, sizeof(float));
  409.   struct PeakHold *ptr = PeakHold_Create(window_size);
  410.  
  411.   PeakHold_BruteSearch(data, brute_calc, sample_count, window_size);
  412.   PeakHold_HoldSearch(ptr, data, hold_calc, sample_count, window_size);
  413.  
  414.   for(size_t i = 0; i < sample_count; ++i) {
  415.     if(!FloatNearEq(brute_calc[i], hold_calc[i]))
  416.       printf("%f: %f .. %f\n", data[i], brute_calc[i], hold_calc[i]);
  417.   }
  418.  
  419.   PeakHold_Destroy(ptr);
  420.   free(data);
  421.   free(brute_calc);
  422.   free(hold_calc);
  423. }
  424.  
  425. static void TestPeakHold(){
  426.   /* ValidatePeakHold(10.f, 44100); */
  427.   TimePeakHold(100, 44100);
  428. }
  429.        
  430. int main() {
  431.   time_t t;
  432.   srand((unsigned) time(&t));
  433.   TestPeakHold();
  434.   return 0;
  435. }
Add Comment
Please, Sign In to add comment