| @@ -950,3 +950,261 @@ void tPeriodDetection_setTolerance (tPeriodDetection* pd, float tolerance · 949 unmodified lines |
| 950 | if (tolerance < 0.0f) p->tolerance = 0.0f; | 950 | if (tolerance < 0.0f) p->tolerance = 0.0f; |
| 951 | else p->tolerance = tolerance; | 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 | } |