| @@ -950,3 +950,261 @@ void tPeriodDetection_setTolerance (tPeriodDetection* pd, float tolerance · 949 unmodified lines |
| 950 | 950 | if (tolerance < 0.0f) p->tolerance = 0.0f; |
| 951 | 951 | else p->tolerance = tolerance; |
| 952 | 952 | } |
| 953 | + |
| 954 | + |
| 955 | +void tZeroCrossing2_init (tZeroCrossing2* const zc) |
| 956 | +{ |
| 957 | + tZeroCrossing2_initToPool(zc, &leaf.mempool); |
| 958 | +} |
| 959 | + |
| 960 | +void tZeroCrossing2_initToPool (tZeroCrossing2* const zc, tMempool* const mp) |
| 961 | +{ |
| 962 | + _tMempool* m = *mp; |
| 963 | + _tZeroCrossing2* z = *zc = (_tZeroCrossing2*) mpool_alloc(sizeof(_tZeroCrossing2), m); |
| 964 | + z->mempool = m; |
| 965 | + |
| 966 | + z->_leading_edge = INT_MIN; |
| 967 | + z->_trailing_edge = INT_MIN; |
| 968 | + z->_width = 0.0f; |
| 969 | +} |
| 970 | + |
| 971 | +void tZeroCrossing2_free (tZeroCrossing2* const zc) |
| 972 | +{ |
| 973 | + _tZeroCrossing2* z = *zc; |
| 974 | + |
| 975 | + mpool_free((char*)z, z->mempool); |
| 976 | +} |
| 977 | + |
| 978 | +void tZeroCrossing2_updatePeak(tZeroCrossing2* const zc, float s, int pos) |
| 979 | +{ |
| 980 | + _tZeroCrossing2* z = *zc; |
| 981 | + |
| 982 | + z->_peak = fmax(s, z->_peak); |
| 983 | + if ((z->_width == 0.0f) && (s < (z->_peak * 0.3f))) |
| 984 | + z->_width = pos - z->_leading_edge; |
| 985 | +} |
| 986 | + |
| 987 | +int tZeroCrossing2_period(tZeroCrossing2* const zc, tZeroCrossing2* const next) |
| 988 | +{ |
| 989 | + _tZeroCrossing2* z = *zc; |
| 990 | + _tZeroCrossing2* n = *next; |
| 991 | + |
| 992 | + return n->_leading_edge - z->_leading_edge; |
| 993 | +} |
| 994 | + |
| 995 | +float tZeroCrossing2_fractionalPeriod(tZeroCrossing2* const zc, tZeroCrossing2* const next) |
| 996 | +{ |
| 997 | + _tZeroCrossing2* z = *zc; |
| 998 | + _tZeroCrossing2* n = *next; |
| 999 | + |
| 1000 | + // Get the start edge |
| 1001 | + float prev1 = z->_before_crossing; |
| 1002 | + float curr1 = z->_after_crossing; |
| 1003 | + float dy1 = curr1 - prev1; |
| 1004 | + float dx1 = -prev1 / dy1; |
| 1005 | + |
| 1006 | + // Get the next edge |
| 1007 | + float prev2 = n->_before_crossing; |
| 1008 | + float curr2 = n->_after_crossing; |
| 1009 | + float dy2 = curr2 - prev2; |
| 1010 | + float dx2 = -prev2 / dy2; |
| 1011 | + |
| 1012 | + // Calculate the fractional period |
| 1013 | + float result = n->_leading_edge - z->_leading_edge; |
| 1014 | + return result + (dx2 - dx1); |
| 1015 | +} |
| 1016 | + |
| 1017 | +int tZeroCrossing2_getWidth(tZeroCrossing2* const zc) |
| 1018 | +{ |
| 1019 | + _tZeroCrossing2* z = *zc; |
| 1020 | + |
| 1021 | + return z->_width; |
| 1022 | +} |
| 1023 | + |
| 1024 | +int tZeroCrossing2_isSimilar(tZeroCrossing2* const zc, tZeroCrossing2* const next) |
| 1025 | +{ |
| 1026 | + _tZeroCrossing2* z = *zc; |
| 1027 | + _tZeroCrossing2* n = *next; |
| 1028 | + |
| 1029 | + int similarPeak = fabs(z->_peak - n->_peak) <= ((1.0f - PULSE_HEIGHT_DIFF) * fmax(fabs(z->_peak), fabs(n->_peak))); |
| 1030 | + |
| 1031 | + int similarWidth = fabs(z->_width - n->_width) <= ((1.0f - PULSE_WIDTH_DIFF) * fmax(fabs(z->_width), fabs(n->_width))); |
| 1032 | + |
| 1033 | + return similarPeak && similarWidth; |
| 1034 | +} |
| 1035 | + |
| 1036 | + |
| 1037 | +static void update_state(tZeroCrossings* const zc, float s); |
| 1038 | +static void shift(tZeroCrossings* const zc, int n); |
| 1039 | +static void reset(tZeroCrossings* const zc); |
| 1040 | + |
| 1041 | +void tZeroCrossings_init (tZeroCrossings* const zc, int windowSize, float hysteresis) |
| 1042 | +{ |
| 1043 | + tZeroCrossings_initToPool(zc, windowSize, hysteresis, &leaf.mempool); |
| 1044 | +} |
| 1045 | + |
| 1046 | +void tZeroCrossings_initToPool (tZeroCrossings* const zc, int windowSize, float hysteresis, tMempool* const mp) |
| 1047 | +{ |
| 1048 | + _tMempool* m = *mp; |
| 1049 | + _tZeroCrossings* z = *zc = (_tZeroCrossings*) mpool_alloc(sizeof(_tZeroCrossings), m); |
| 1050 | + z->mempool = m; |
| 1051 | + |
| 1052 | + z->_hysteresis = hysteresis; |
| 1053 | + int bits = CHAR_BIT * sizeof(unsigned int); |
| 1054 | + z->_window_size = fmax(2, (windowSize + bits - 1) / bits) * bits; |
| 1055 | + |
| 1056 | + // The current ring buffer and indexing implementation for this is less efficient than the original source |
| 1057 | + // May be worth porting over the ring buffer class from https://github.com/cycfi/q/tree/develop |
| 1058 | + z->_info = (_tZeroCrossing2*) mpool_alloc(sizeof(_tZeroCrossing2) * (z->_window_size / 2), m); |
| 1059 | + z->index = -1; |
| 1060 | +} |
| 1061 | + |
| 1062 | +void tZeroCrossings_free (tZeroCrossings* const zc) |
| 1063 | +{ |
| 1064 | + _tZeroCrossings* z = *zc; |
| 1065 | + |
| 1066 | + mpool_free((char*)z, z->mempool); |
| 1067 | +} |
| 1068 | + |
| 1069 | +int tZeroCrossings_tick(tZeroCrossings* const zc, float s) |
| 1070 | +{ |
| 1071 | + _tZeroCrossings* z = *zc; |
| 1072 | + |
| 1073 | + return z->_state; |
| 1074 | +} |
| 1075 | + |
| 1076 | +int tZeroCrossings_getState(tZeroCrossings* const zc) |
| 1077 | +{ |
| 1078 | + _tZeroCrossings* z = *zc; |
| 1079 | + |
| 1080 | + return z->_state; |
| 1081 | +} |
| 1082 | + |
| 1083 | +int tZeroCrossings_getNumEdges(tZeroCrossings* const zc) |
| 1084 | +{ |
| 1085 | + _tZeroCrossings* z = *zc; |
| 1086 | + |
| 1087 | + return z->_num_edges; |
| 1088 | +} |
| 1089 | + |
| 1090 | +int tZeroCrossings_getCapacity(tZeroCrossings* const zc) |
| 1091 | +{ |
| 1092 | + _tZeroCrossings* z = *zc; |
| 1093 | + |
| 1094 | + return z->_window_size / 2; |
| 1095 | +} |
| 1096 | + |
| 1097 | +int tZeroCrossings_getFrame(tZeroCrossings* const zc) |
| 1098 | +{ |
| 1099 | + _tZeroCrossings* z = *zc; |
| 1100 | + |
| 1101 | + return z->_frame; |
| 1102 | +} |
| 1103 | + |
| 1104 | +int tZeroCrossings_getWindowSize(tZeroCrossings* const zc) |
| 1105 | +{ |
| 1106 | + _tZeroCrossings* z = *zc; |
| 1107 | + |
| 1108 | + return z->_window_size; |
| 1109 | +} |
| 1110 | + |
| 1111 | +int tZeroCrossings_isReady(tZeroCrossings* const zc) |
| 1112 | +{ |
| 1113 | + _tZeroCrossings* z = *zc; |
| 1114 | + |
| 1115 | + return z->_ready; |
| 1116 | +} |
| 1117 | + |
| 1118 | +float tZeroCrossings_getPeak(tZeroCrossings* const zc) |
| 1119 | +{ |
| 1120 | + _tZeroCrossings* z = *zc; |
| 1121 | + |
| 1122 | + return fmax(z->_peak, z->_peak_update); |
| 1123 | +} |
| 1124 | + |
| 1125 | +int tZeroCrossings_isReset(tZeroCrossings* const zc) |
| 1126 | +{ |
| 1127 | + _tZeroCrossings* z = *zc; |
| 1128 | + |
| 1129 | + return z->_frame == 0; |
| 1130 | +} |
| 1131 | + |
| 1132 | +static void update_state(tZeroCrossings* const zc, float s) |
| 1133 | +{ |
| 1134 | + _tZeroCrossings* z = *zc; |
| 1135 | + |
| 1136 | + int capacity = z->_window_size / 2; |
| 1137 | + if (z->_ready) |
| 1138 | + { |
| 1139 | + shift(zc, capacity); |
| 1140 | + z->_ready = 0; |
| 1141 | + z->_peak = z->_peak_update; |
| 1142 | + z->_peak_update = 0.0f; |
| 1143 | + } |
| 1144 | + |
| 1145 | + if (z->_num_edges >= capacity) |
| 1146 | + reset(zc); |
| 1147 | + |
| 1148 | + if (s > 0.0f) |
| 1149 | + { |
| 1150 | + if (!z->_state) |
| 1151 | + { |
| 1152 | + ++z->index; |
| 1153 | + _tZeroCrossing2 crossing = z->_info[z->index]; |
| 1154 | + crossing._before_crossing = z->_prev; |
| 1155 | + crossing._after_crossing = s; |
| 1156 | + crossing._peak = s; |
| 1157 | + crossing._leading_edge = (int) z->_frame; |
| 1158 | + ++z->_num_edges; |
| 1159 | + z->_state = 1; |
| 1160 | + } |
| 1161 | + else |
| 1162 | + { |
| 1163 | + tZeroCrossing2 ptr = &z->_info[z->index]; |
| 1164 | + tZeroCrossing2_updatePeak(&ptr, s, z->_frame); |
| 1165 | + } |
| 1166 | + if (s > z->_peak_update) |
| 1167 | + { |
| 1168 | + z->_peak_update = s; |
| 1169 | + } |
| 1170 | + } |
| 1171 | + else if (z->_state && (s < z->_hysteresis)) |
| 1172 | + { |
| 1173 | + z->_state = 0; |
| 1174 | + z->_info[z->index]._trailing_edge = z->_frame; |
| 1175 | + if (z->_peak == 0.0f) |
| 1176 | + z->_peak = z->_peak_update; |
| 1177 | + } |
| 1178 | + |
| 1179 | + z->_prev = s; |
| 1180 | +} |
| 1181 | + |
| 1182 | +static void shift(tZeroCrossings* const zc, int n) |
| 1183 | +{ |
| 1184 | + _tZeroCrossings* z = *zc; |
| 1185 | + |
| 1186 | + _tZeroCrossing2 crossing = z->_info[z->index]; |
| 1187 | + |
| 1188 | + crossing._leading_edge -= n; |
| 1189 | + if (!z->_state) |
| 1190 | + crossing._trailing_edge -= n; |
| 1191 | + int i = 1; |
| 1192 | + for (; i != z->_num_edges; ++i) |
| 1193 | + { |
| 1194 | + int idx = (z->index + i) % (z->_window_size / 2); |
| 1195 | + z->_info[idx]._leading_edge -= n; |
| 1196 | + int edge = (z->_info[idx]._trailing_edge -= n); |
| 1197 | + if (edge < 0.0f) |
| 1198 | + break; |
| 1199 | + } |
| 1200 | + z->_num_edges = i; |
| 1201 | +} |
| 1202 | + |
| 1203 | +static void reset(tZeroCrossings* const zc) |
| 1204 | +{ |
| 1205 | + _tZeroCrossings* z = *zc; |
| 1206 | + |
| 1207 | + z->_num_edges = 0; |
| 1208 | + z->_state = 0; |
| 1209 | + z->_frame = 0; |
| 1210 | +} |